/*
 * @Description: 地图引擎入口主文件
 * @Date: 2022-01-04 14:26:40
 * @Author: yeming
 */

/* eslint-disable no-undef */
/* eslint-disable array-bracket-spacing */
/* eslint-disable space-before-function-paren */

// 注：过程中总结发现
// 1、ol 实质使用的是webGis
// 2、olcs 中才会实际用到 cesium.js (olcs/OLCesium.js)
// 以下为第三方库
// import * as turf from '@turf/turf'
// import { randomPoint } from '@turf/turf'

// 以下均来自ol
import { transform, fromUserCoordinate, getUserProjection } from 'ol/proj.js'
import { LineString } from 'ol/geom'
import { getArea, getLength, getDistance } from 'ol/sphere'
import { distance as coordinateDistance } from 'ol/coordinate.js'
import { fromLonLat, toLonLat } from 'ol/proj.js'
import OlInteractionTranslate from 'ol/interaction/Translate'
import TrackLine from './work/model/line/trackLine.js'

import { MAP_MODE, getMapMode, setMapMode } from './common/mapMode.js'
import { MAP_TYPE, getMapType, setMapType } from './common/mapType.js'
import { LINE_TYPE, getLineType, setLineType } from './common/lineType.js'
import { UAV, setUavActiveSessionId, getUavActiveSessionId, setUavLocked, getUavLocked, setPodLocked, getPodLocked } from './common/uav.js'
import { setOlStylePoint } from './common/olStyle.js'
import Cookie from '../common/scripts/cookie.js'

// 以下为自定义功能模块
import Ol2d from './work/ol2d.js'
import Ol3d from './work/ol3d.js'
import Uav from './work/modules/uav.js'
import NoFlyZone from './work/modules/noFlyZone.js'
import TakeOffPoint from './work/modules/takeOffPoint.js'
import GivePointFly from './work/modules/givePointFly.js'
import Simulation from './work/modules/simulation.js'
import tileLayers from './layers/layer.js'
import { vectorLayers, uavLayer, forbidLayer, electricFenceLayer, customPointLayer, pointLayer, lineLayer, circleLayer, polygonLayer, simulationLayer } from './layers/vector.js'
import { Point, Point3d } from './work/model/point.js'
import { Line, Line3d } from './work/model/line/line.js'
import { Circle, Circle3d } from './work/model/circle.js'
import { Polygon, Polygon3d } from './work/model/polygon.js'
import MouseTips from './work/common/mouseTips.js'
import { sendLatlngFromLeftMouseClickToJava, sendLatlngFromRightMouseClickToJava, sendLatlngFromLeftMouseDoubleClickToJava, sendDataFromTranslateStartToJava, sendDataFromTranslatingToJava, sendDataFromTranslateEndToJava, sendLatlngFromMouseMoveToJava, sendMapCenterAndZoomToJava } from './interface.js'
import CustomPoint from './work/modules/customPoint.js'
import ElectricFence from './work/modules/electricFence.js'
// import { olcsMapLog } from './common/log.js' // 管理日志打印

import { color2Rgba } from './work/common/color2rgba.js'
// console.log(color2Rgba('#fff'))

window.Cesium = require('cesium/Cesium') // 此处将cesium.js引入，供全局使用（其中3D地图，OLCesium.js中会用到）

// 是否为测试
let isOlcsTest = Cookie.get('olcsTest') || 0

// 常量
const GIVE_POINT = './static/olcsMap/img/give_point_to_fly.png'
const FIREMAN_POINT = './static/olcsMap/img/airGroundCoo/fireman.png'
const DEFALUT_3D_TERRAIN = 'http://127.0.0.1:7003/Home/TerrainTile/'
const DEFAULT_ZOOM = 15 // 缩放级别
const EARTH_RADIUS = 6378.137 // 地球半径(km)

class OlcsMap {
  constructor(options) {
    this.options = options
    this.tileLayers = tileLayers // 地图瓦片等相关图层
    this.vectorLayers = vectorLayers // 点线面等地图上添加内容物的相关图层
    this.layers = tileLayers.concat(vectorLayers)
    this.ol2d = new Ol2d(options, this.layers, this.type) // 2D实例
    this.ol3d = new Ol3d(this.ol2d, options) // 3D实例
    this.map2d = this.ol2d.map2d // Map
    this.map3d = this.ol3d.map3d // OLCesium
    this.scene = this.map3d.getCesiumScene()
    this.entities = this.ol3d.entities // 3d 实体(全局)
    this.mapMode = getMapMode() // 地图类型
    this.terrainUrl = DEFALUT_3D_TERRAIN // 3d高程url

    // 以下均为各矢量图层数据源，用来添加Feature
    this.uavSource = uavLayer.getSource()
    this.forbidSource = forbidLayer.getSource()
    this.customPointSource = customPointLayer.getSource()
    this.electricFenceSource = electricFenceLayer.getSource()
    this.pointSource = pointLayer.getSource()
    this.lineSource = lineLayer.getSource()
    this.circleSource = circleLayer.getSource()
    this.simulationSource = simulationLayer.getSource()

    // 以下均为全局优化参数
    this.hasTranslate = 0 // 是否已经存在 translateInteraction
    this.currentHoverFeature = null // 鼠标当前hover状态的Feature
    this.translateInteraction = new OlInteractionTranslate() // 拖拽行为

    // 飞机相关
    this.uavMap = new Map() // 无人机 hash Map
    this.uav = new Uav(this.ol2d, this.ol3d, this.uavMap, this.uavSource) // 飞机实例

    // 特殊点相关，自定义点相关（火点、基站等）
    // 起飞点
    this.takeOffPointMap = new Map()
    this.takeOffPointInstance = new TakeOffPoint(this.ol2d, this.takeOffPointMap, this.customPointSource)
    // 指点飞行的点
    this.givePointMap = new Map()
    // 指点飞行点的盘旋圈
    this.givePointCircleMap = new Map()
    this.givePointFlyInstance = new GivePointFly(this.ol2d, this.ol3d, this.givePointMap, this.givePointCircleMap, this.customPointSource)
    // 自定义点（如：精确着火点，粗略着火点等）
    this.customPointMap = new Map()
    this.customPointCircleMap = new Map() // 独立的点的拥有的circle，如：漫游基站，指点飞行等
    this.customPointInstance = new CustomPoint(this.ol2d, this.ol3d, this.customPointMap, this.customPointCircleMap, this.customPointSource)

    // 仿真相关
    this.simulationTakeOffPointMap = new Map() // 起飞点
    this.simulationCenterPointMap = new Map() // 区域中心点
    this.simulationPolygonMap = new Map() // 区域多边形
    this.simulationInstance = new Simulation(this.ol2d, this.simulationTakeOffPointMap, this.simulationCenterPointMap, this.simulationPolygonMap, this.simulationSource)

    // 禁飞区相关
    this.noFlyZoneInstance = new NoFlyZone(this.ol2d, this.forbidSource)

    // 电子围栏相关
    this.circleElectricFenceMap = new Map() // 圆形电子围栏 Map
    // this.circleElectricFencePointMap = new Map() // 圆形电子围栏中心点
    this.canSetCircleElectricFenceRadius = false // 是否可设置圆形电子围栏半径
    this.polygonElectricFenceMap = new Map() // 多边形电子围栏 Map
    this.polygonElectricFencePointsMap = new Map() // 当前多边形电子围栏各顶点 Map
    this.electricFenceInstance = new ElectricFence(this.ol2d, this.circleElectricFenceMap, this.polygonElectricFenceMap, this.polygonElectricFencePointsMap, this.electricFenceSource)
    // 电子围栏自测需要数据，可移除
    this.circleElectricFenceSessionId = 0 // 当前圆形电子围栏sessionId
    this.polygonElectricFenceSessionId = 0 // 当前多边形电子围栏 sessionId

    // 鼠标提示相关
    this.mouseTipsMap = new Map() // 提示 hash Map
    this.mouseTips = null
    this.oldMapModeMouseTipsContent = '' // 只存储地图模式切换返回的鼠标提示内容，当编辑模式下从Feature上移出再次显示上一状态提示
    this.mouseTipsContent = '' // 鼠标提示内容，包括编辑模式下鼠标移入Feature上的提示

    // 航线相关
    this.hasLandLine = false // 固定翼有 旋翼无
    this.airlinePointMap = new Map() // 航线点 Map
    this.airlineTaskLineMap = new Map() // 任务线 Map
    this.airlineLandLineMap = new Map() // 降落线 Map
    this.airlineTaskLineString = null // 任务线 LineString，接收点生成线
    this.airlineLandLineString = null // 降落线 LineString，接收点生成线
    this.airlineCircleMap = new Map() // 航点盘旋圈 Map
    this.airlineGuideLineMap = new Map() // 航线引导线 Map

    // 以下均为事件回掉函数
    this.switchMapTypeCallback = options.switchMapTypeCallback
    this.updatePanelCallback = options.updatePanelCallback // 仪表盘回调函数
    this.singleClickCallback = options.singleClickCallback // 鼠标单击回调函数
    this.mouseMoveCallback = options.mouseMoveCallback // 鼠标移动回调函数
    this.mapZoomCallback = options.mapZoomCallback // 地图缩放回调函数

    this.idArr = [] // 存储新增点的id，模拟修改指定点属性

    // --------------------- 鼠标交互事件相关 ---------------------
    let that = this
    // 鼠标滑动事件监听，划过点、线、面等Feature变化鼠标样式，在地图上默认箭头
    this.map2d.on('pointermove', function (e) {
      if (e.dragging) return false; // 拖拽过程中阻止以下逻辑，否则拖拽会被打断

      let coordinate = e.coordinate
      let latlng = toLonLat(coordinate)
      latlng[2] = 80.0 // 高度默认全部给0，大于0为3d
      that.mapMode = getMapMode()
      // 切换地图模式更新鼠标提示
      that.changeMouseTipsPosition(that.mapMode, coordinate, that.mouseTipsContent)
      that.mouseMoveCallback(latlng)
      // send data to java
      sendLatlngFromMouseMoveToJava(that.mapMode, latlng)

      let pixel = that.map2d.getEventPixel(e.originalEvent) // 获取点击的像素内容
      let hasFeature = that.map2d.hasFeatureAtPixel(pixel) // 判断划过的像素上是否存在 Feature
      let featureType = ""
      let propertyIsEdit = 0
      if (hasFeature) {
        // 鼠标划过地图返回undefined，划过点、线、面分别返回对应Feature
        that.currentHoverFeature = that.map2d.forEachFeatureAtPixel(pixel, feature => { return feature }) // 查询方式有很多
        if (!that.currentHoverFeature) return false

        featureType = that.currentHoverFeature.getGeometry().getType() // 几何图形的原始类型 Point LineString 等
        propertyIsEdit = that.currentHoverFeature.getProperties()['isEdit'] // 当前Feature是否可编辑
        let propertyType = that.currentHoverFeature.getProperties()['type'] // 当前Feature的自定义属性

        // 拖拽交互条件优化，由点本身属性来决定是否可编辑，抛弃掉地图全局控制可精确到具体的某一个点（很重要）
        if (featureType == "Point") {
          // 非编辑模式下伸出食指，编辑模式下变手掌
          that.map2d.getTargetElement().style.cursor = 'pointer' // 小手
          if (propertyIsEdit) {
            that.mouseTipsContent = '鼠标左键按下可拖拽'
            that.addOlInteractionTranslate()
          }
        } else {
          that.mouseTipsContent = that.oldMapModeMouseTipsContent
          that.map2d.getTargetElement().style.cursor = ''
          that.removeOlInteractionTranslate()
        }

        // 任务线允许插入 type 1 任务线 2 降落线
        if (featureType == "LineString" && propertyType <= 1 && propertyIsEdit) {
          that.mouseTipsContent = '鼠标左键按下可插入点'
          that.map2d.getTargetElement().style.cursor = 'crosshair' // 十字瞄准线
        }
      } else {
        that.mouseTipsContent = that.oldMapModeMouseTipsContent
        that.map2d.getTargetElement().style.cursor = ''
      }

      // 绘制电子围栏模式
      if (that.mapMode == 600 && that.canSetCircleElectricFenceRadius) {
        let currentCircleElectricFence = that.circleElectricFenceMap.get(that.circleElectricFenceSessionId)
        if (currentCircleElectricFence) {
          let feature = currentCircleElectricFence.item2d.feature
          let latlng = feature.getProperties()['latlng']
          let centerCoordinate = fromLonLat(latlng)
          let radius = coordinateDistance(fromUserCoordinate(centerCoordinate, getUserProjection()), fromUserCoordinate(coordinate, getUserProjection()))
          that.drawCircleElectricFence(that.circleElectricFenceSessionId, latlng, radius, null)
        }
      }
    })

    // 监听地图移动结束
    this.map2d.on('moveend', function (e) {
      let frameState = e.frameState
      let zoom = Math.floor(frameState.viewState.zoom)
      let mapCenterCoordinate = that.map2d.getView().getCenter()
      let mapCenterLatlng = toLonLat(mapCenterCoordinate)
      mapCenterLatlng[2] = 80.0 // 高度默认全部给0，大于0为3d

      // 当地图切换为2d或者db类型时更新地图，主要解决地图画布不更新问题（2d只占半屏）
      let mapType = that.getMapType()
      if (mapType == 'db' || mapType == '2d') that.map2d.updateSize()

      // 存储地图中心点位置作为下次进入地图中心点位置
      Cookie.set('latlng', JSON.stringify(mapCenterLatlng), 10 * 365 * 24 * 60 * 60 * 1000)
      sendMapCenterAndZoomToJava(mapCenterLatlng, zoom) // 发送数据给java，详情查看interface.js
      that.mapZoomCallback(zoom)
    })

    // 鼠标左键点击
    this.map2d.on('singleclick', function (e) {
      let coordinate = e.coordinate; // 鼠标单击点的坐标（投影坐标）
      let latlng = toLonLat(coordinate)
      latlng[2] = 80.0 // 高度默认全部给0，大于0为3d
      that.mapMode = getMapMode() // 获取地图模式 如：100-自定义点

      that.singleClickCallback(latlng)

      let pixel = that.map2d.getEventPixel(e.originalEvent) // 获取点击的像素内容
      let hasFeature = that.map2d.hasFeatureAtPixel(pixel); // 判断划过的像素上是否存在 Feature
      if (!hasFeature) sendLatlngFromLeftMouseClickToJava(that.mapMode, latlng, 1) // 数据（地图模式，经纬度坐标，append）回传给java
      else {
        let featureType = that.currentHoverFeature.getGeometry().getType()
        if (featureType == 'LineString') sendLatlngFromLeftMouseClickToJava(that.mapMode, latlng, 2)
        else sendLatlngFromLeftMouseClickToJava(that.mapMode, latlng, 1) // 数据（地图模式，经纬度坐标，insert）回传给java
      }

      // 圆形电子围栏
      if (that.mapMode == 600) that.canSetCircleElectricFenceRadius = !that.canSetCircleElectricFenceRadius
      else that.canSetCircleElectricFenceRadius = false

      // 以下为测试操作
      if (!isOlcsTest) return false

      // 指点飞行模式 sessionId, latlng, image, radius, style
      if (that.mapMode == 10) that.addGivePointToFly(1646191329544, latlng, '', 160, null)
      // 自定义点模式
      if (that.mapMode == 100) {
        // sessionId, latlng, type, image, radius, isEdit
        // 自定义点类型 1-精确着火点 2-粗略着火点 3-警察位置 4-消防员位置 5-漫游基站位置
        let sessionId = new Date().getTime()
        // that.addCustomPoint(sessionId, latlng, 1, GIVE_POINT, 0, 1)
        // that.addCustomPoint(sessionId, latlng, 2, GIVE_POINT, 0, 1)
        // that.addCustomPoint(sessionId, latlng, 3, GIVE_POINT, 0, 1)
        that.addCustomPoint(sessionId, latlng, 4, GIVE_POINT, 0, 1, null)
        // that.addCustomPoint(sessionId, latlng, 5, GIVE_POINT, 160, 1, null)
      }
      // 添加起飞点模式
      if (that.mapMode == 500) that.addTakeOffPoint(1646794856271, latlng)
      // 仿真区域中心点
      if (that.mapMode == 550) that.addSimulationArea(1648868306126, latlng, 300, 300)
      // 仿真起飞点
      if (that.mapMode == 551) that.addSimulationTakeOffPoint(1648868306127, latlng)
      // 绘制圆形电子围栏模式
      if (that.mapMode == 600) {
        // sessionId, latlng, radius, style
        that.drawCircleElectricFence(that.circleElectricFenceSessionId, latlng, 0, null)
      }
      // 绘制多边形电子围栏模式
      if (that.mapMode == 601) {
        that.drawPolygonElectricFence(that.polygonElectricFenceSessionId, latlng)
      }
      // 单线飞行模式 1-可编辑 2-不可编辑
      if (that.mapMode == 1 || that.mapMode == 2) {
        // 插入航点
        let pixel = that.map2d.getEventPixel(e.originalEvent) // 获取点击的像素内容
        let hasFeature = that.map2d.hasFeatureAtPixel(pixel); // 判断划过的像素上是否存在 Feature
        // 点击的Feature是线时，允许插入
        if (hasFeature) {
          let featureType = that.currentHoverFeature.getGeometry().getType()
          let propertyId = that.currentHoverFeature.getProperties()['id']
          let propertyType = that.currentHoverFeature.getProperties()['type'] // 当前Feature（如：线）自定义属性type 1 任务 2 降落
          if (featureType == 'LineString') {
            // that.currentHoverFeature = that.map2d.forEachFeatureAtPixel(pixel, feature => { return feature }) // 查询方式有很多
            if (propertyType == 1 || propertyType == 100) { // 任务线允许插入
              // id, coordinate, isHover, radius = 0, isEdit = 1, stfeautre, parentType
              let id = new Date().getTime()
              that.insertPoint(id, latlng, 1, 160, 1, { 'fillColor': 'rgba(255, 153, 255, 1)', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 }, propertyId, propertyType)
            }
          } else { // 禁飞区上打点（圆形、多边形）
            // let pointList = [{{id, pointType 0-起始点 1-任务航点 2-降高 3-刹车 4-降落, num 编号, numPrefix S-起始点 L-降落点, latlng, isHover 是否盘旋（决定是否渲染radius）, radius, speed, ponitStyle }}]
            let pointStyle = { 'fillColor': 'rgba(255, 153, 255, 1)', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 }
            if (that.idArr.length <= 0) {
              // 单线飞行模式模拟
              let step = 0.00135;
              let { ...s1 } = { id: 1646191623528, pointType: 0, num: 1, numPrefix: 'S', latlng: latlng, isHover: 1, radius: 160, speed: 10, pointStyle: pointStyle };
              let { ...e1 } = { id: 1646191726908, pointType: 2, num: 1, numPrefix: 'L', latlng: [latlng[0] - step * 10, latlng[1]], isHover: 1, radius: 160, speed: 10, pointStyle: pointStyle };
              let { ...e2 } = { id: 1646191769685, pointType: 3, num: 2, numPrefix: 'L', latlng: [latlng[0] - step * 10, latlng[1] + step * 3.8], isHover: 0, radius: 0, speed: 10, pointStyle: pointStyle };
              let { ...e3 } = { id: 1646191894463, pointType: 4, num: 3, numPrefix: 'L', latlng: [latlng[0] - step * 10, latlng[1] + step * 5], isHover: 0, radius: 0, speed: 10, pointStyle: pointStyle };
              let startPointList = [s1, e1, e2, e3]

              that.addSingleLineToFly(1646191322345, 1, startPointList)
              that.idArr.push(1646191623528)
            } else {
              let id = new Date().getTime()
              let currentLine = that.airlinePointMap.get(1646191322345)
              let taskLength = currentLine.taskPoint.length // 每次打点之前计算长度
              let clickPointList = [{ id: id, pointType: 1, num: taskLength + 1, numPrefix: '', latlng: latlng, isHover: 0, radius: 160, speed: 10, pointStyle: pointStyle }]
              that.addSingleLineToFly(1646191322345, 1, clickPointList)
              that.idArr.push(id)
            }
          }
        } else {
          // let pointList = [{{id, pointType 0-起始点 1-任务航点 2-降高 3-刹车 4-降落, num 编号, numPrefix S-起始点 L-降落点, latlng, isHover 是否盘旋（决定是否渲染radius）, radius, speed, ponitStyle }}]
          let pointStyle = { 'fillColor': 'rgba(255, 153, 255, 1)', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 }
          if (that.idArr.length <= 0) {
            // 单线飞行模式模拟
            let step = 0.00135;
            let { ...s1 } = { id: 1646191623528, pointType: 0, num: 1, numPrefix: 'S', latlng: latlng, isHover: 1, radius: 160, speed: 10, pointStyle: pointStyle };
            let { ...e1 } = { id: 1646191726908, pointType: 2, num: 1, numPrefix: 'L', latlng: [latlng[0] - step * 10, latlng[1]], isHover: 1, radius: 160, speed: 10, pointStyle: pointStyle };
            let { ...e2 } = { id: 1646191769685, pointType: 3, num: 2, numPrefix: 'L', latlng: [latlng[0] - step * 10, latlng[1] + step * 3.8], isHover: 0, radius: 0, speed: 10, pointStyle: pointStyle };
            let { ...e3 } = { id: 1646191894463, pointType: 4, num: 3, numPrefix: 'L', latlng: [latlng[0] - step * 10, latlng[1] + step * 5], isHover: 0, radius: 0, speed: 10, pointStyle: pointStyle };
            let startPointList = [s1, e1, e2, e3]
            // let startPointList = [s1]

            that.addSingleLineToFly(1646191322345, 1, startPointList)
            that.idArr.push(1646191623528)
          } else {
            let id = new Date().getTime()
            let currentLine = that.airlinePointMap.get(1646191322345)
            let taskLength = currentLine.taskPoint.length // 每次打点之前计算长度
            let clickPointList = [{ id: id, pointType: 1, num: taskLength + 1, numPrefix: '', latlng: latlng, isHover: 0, radius: 160, speed: 10, pointStyle: pointStyle }]
            that.addSingleLineToFly(1646191322345, 1, clickPointList)
            that.idArr.push(id)
          }
        }
      }
    })

    // 鼠标左键双击
    this.map2d.on('dblclick', function (e) {
      e.preventDefault(); // 阻止默认行为（双击缩放地图）

      let coordinate = e.coordinate; // 鼠标双击点的坐标（投影坐标）
      let latlng = toLonLat(coordinate)
      latlng[2] = 80.0
      that.mapMode = getMapMode()
      sendLatlngFromLeftMouseDoubleClickToJava(that.mapMode, latlng) // 数据（地图模式，点击地图经纬度坐标）回传给java
    })
    // 鼠标右键点击
    this.map2d.on('contextmenu', function (e) {
      e.preventDefault(); // 阻止默认行为（右键弹出默认菜单）

      that.mapMode = getMapMode()
      let coordinate = e.coordinate; // 鼠标单击点的坐标（投影坐标）
      let latlng = toLonLat(coordinate)
      latlng[2] = 80.0
      let position = [e.originalEvent.x, e.originalEvent.y] // 屏幕位置[x, y], 相对于左上角[0, 0]
      let pixel = that.map2d.getEventPixel(e.originalEvent) // 获取点击的像素内容
      let hasFeature = that.map2d.hasFeatureAtPixel(pixel); // 判断划过的像素上是否存在 Feature
      let type = 0 // 1 航点 2 其他点 （只有航线有上传操作，其他点没有）
      let pointData = {}

      let featureType = null
      if (that.currentHoverFeature) featureType = that.currentHoverFeature.getGeometry().getType()
      if (hasFeature && featureType == 'Point') {
        let properties = that.currentHoverFeature.getProperties()
        let propertyParentType = properties.parentType
        // 单线飞行或者机载航线（航点）
        if (propertyParentType == 1 || propertyParentType == 100) {
          type = 1
          pointData = { 'id': properties.id, 'latlng': properties.latlng, 'type': properties.type, 'num': properties.index * 1 + 1, 'prefix': properties.prefix, 'isHover': properties.isHover, 'radius': properties.radius, 'isEdit': properties.isEdit, 'style': properties.style, 'sessionId': properties.parentId, 'lineType': properties.parentType }
        } else {
          type = 2
          pointData = { 'id': properties.id, 'latlng': properties.latlng, 'type': properties.type, 'image': properties.image, 'radius': properties.radius, 'isEdit': properties.isEdit, 'style': properties.style }
        }
      }
      // 数据（地图模式，点击地图经纬度坐标）回传给java
      sendLatlngFromRightMouseClickToJava(that.mapMode, latlng, type, position, pointData)
      console.log(that.mapMode, latlng, type, position, pointData)
    })

    // 鼠标拖动Feature开始
    this.translateInteraction.on('translatestart', function (f) {
      that.mapMode = getMapMode() // 获取地图模式 如：100-自定义点
      let latlng = toLonLat(f.coordinate)
      let feature = f.features.getArray()[0]
      let properties = feature.getProperties()
      // 当前Feature属性
      let id = properties['id'] // 点id
      let type = properties['type'] // 点type 1-任务 2-降落
      let index = properties['index']
      let isHover = properties['isHover']
      let radius = properties['radius']
      let isEdit = properties['isEdit']
      // 父级属性
      let parentId = properties['parentId'] // 父级id
      let parentType = properties['parentType'] // Map对象类型

      // 发送数据给java
      let data = { id: id, latlng: latlng, pointType: type, index: index, isHover: isHover, radius: radius, isEdit: isEdit, parentId: parentId, parentType: parentType }
      sendDataFromTranslateStartToJava(that.mapMode, data)
    })

    // 鼠标拖动Feature过程中
    this.translateInteraction.on('translating', function (f) {
      that.mapMode = getMapMode() // 获取地图模式 如：100-自定义点
      let latlng = toLonLat(f.coordinate)
      latlng[2] = 80.0

      let feature = f.features.getArray()[0]
      let properties = feature.getProperties()
      // let featureType = feature.getGeometry().getType() // 此type取的是Feature的原始类型（Point、LineString、Circle等）
      let id = properties['id'] // 点id
      let type = properties['type'] // 点type 1-任务 2-降落
      let radius = properties['radius']
      let isEdit = properties['isEdit']
      let index = properties['index'] // 点索引
      let isHover = properties['isHover']
      // 父级属性
      let parentId = properties['parentId'] // 父级id
      let parentType = properties['parentType'] // Map对象类型

      // 发送数据给java
      let data = { id: id, latlng: latlng, pointType: type, index: index, isHover: isHover, radius: radius, isEdit: isEdit, parentId: parentId, parentType: parentType }
      sendDataFromTranslatingToJava(that.mapMode, data)

      // 单线飞行航点拖动 0-起始点 1-任务航点 2-降高 3-刹车 4-降落
      if (type <= 4) that.changePointPosition(id, latlng, type, index, isHover, radius, parentId, parentType)

      // 自定义点拖动 customType 1-精确着火点 2-粗略着火点 3-警察位置 4-消防员位置 5-漫游基站位置
      if (type == 6) that.updateCustomPoint(id, latlng, radius, null)

      // 多边形电子围栏各顶点拖动
      if (type == 11) that.updatePolygonElectricFencePoint(latlng, index, parentId)
    })

    // 鼠标拖动Feature结束
    this.translateInteraction.on('translateend', function (f) {
      that.mapMode = getMapMode() // 获取地图模式 如：100-自定义点
      let latlng = toLonLat(f.coordinate)
      let feature = f.features.getArray()[0]
      let properties = feature.getProperties()
      // 当前Feature属性
      let id = properties['id'] // 点id
      let type = properties['type'] // 点type 1-任务 2-降落
      let index = properties['index']
      let isHover = properties['isHover']
      let radius = properties['radius']
      let isEdit = properties['isEdit']
      // 父级属性
      let parentId = properties['parentId'] // 父级id
      let parentType = properties['parentType'] // Map对象类型

      // 发送数据给java
      let data = { id: id, latlng: latlng, pointType: type, index: index, isHover: isHover, radius: radius, isEdit: isEdit, parentId: parentId, parentType: parentType }
      sendDataFromTranslateEndToJava(that.mapMode, data)
    })

    // 3d鼠标操作事件
    this.ol3d.eventHandler3d.setInputAction(function (e) {
      var ray = that.scene.camera.getPickRay(e.position)
      var cartesian = that.scene.globe.pick(ray, that.scene)
      var latlng = null
      if (cartesian) {
        var cartographic = that.scene.globe.ellipsoid.cartesianToCartographic(cartesian)
        latlng = [Number(Cesium.Math.toDegrees(cartographic.longitude).toFixed(6)),
        Number(Cesium.Math.toDegrees(cartographic.latitude).toFixed(6)),
        Number(cartographic.height.toFixed(2))]
        console.log(latlng)
      }

      that.mapMode = getMapMode()
      sendLatlngFromLeftMouseClickToJava(that.mapMode, latlng) // 数据（地图模式，点击地图经纬度坐标）回传给java

      // 以下为测试操作
      if (!isOlcsTest) return false

      // 自定义点
      if (that.mapMode == 100) {
        let sessionId = new Date().getTime()
        that.addCustomPoint(sessionId, latlng, 5, GIVE_POINT, 0, 1, null)
      }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
  }

  // 新增鼠标拖拽交互事件
  addOlInteractionTranslate () {
    if (this.hasTranslate) return false // 只初始化一次
    this.map2d.addInteraction(this.translateInteraction)
    this.hasTranslate = 1
  }

  // 删除鼠标拖拽交互事件
  removeOlInteractionTranslate () {
    if (!this.hasTranslate) return false
    this.map2d.removeInteraction(this.translateInteraction)
    this.hasTranslate = 0
  }

  // --------------------- 地图相关 ---------------------

  // 地图放大
  zoomIn () {
    this.ol2d.zoomIn()
  }

  // 地图缩小
  zoomOut () {
    this.ol2d.zoomOut()
  }

  // 设置地图缩放比例
  setZoom (zoom = DEFAULT_ZOOM) {
    this.ol2d.setZoom(zoom)
  }

  // 获取当前缩放级别
  getZoom () {
    let zoom = Math.floor(this.ol2d.getZoom())
    return zoom
  }

  /**
   * @desc: 设置地图类型 2d 3d db
   * @param {String} val 2d 3d db
   */
  setMapType (val) {
    let type = { 'map2d': 1, 'map3d': 0 }
    const fpsMain = document.getElementById('fpsMain')
    if (val == '2d') {
      type = { 'map2d': 1, 'map3d': 0 }
      fpsMain.style.display = 'none'
      this.ol3d.setMapType3dEnabled(false) // 设置不显示3d
    }
    if (val == '3d') {
      type = { 'map2d': 0, 'map3d': 1 }
      fpsMain.style.display = 'block'
      this.ol3d.setMapType3dEnabled(true) // 显示3d
    }
    if (val == 'db') {
      type = { 'map2d': 1, 'map3d': 1 }
      fpsMain.style.display = 'block'
      this.ol3d.setMapType3dEnabled(true) // 显示3d
    }
    this.switchMapTypeCallback(type) // vue内部回调
    setMapType(val) // 存储类型到全局数据

    // 解决mapType在db或者3d类型下，再切换2d导致的地图画布不更新问题（2d只占半屏）
    let mapCenterCoordinate = this.map2d.getView().getCenter()
    this.locationToSomeWhereByLatlng(toLonLat(mapCenterCoordinate))
    // 高程（云巡走服务器）
    // if (val != '2d') {
    //   this.scene.terrainProvider = new Cesium.CesiumTerrainProvider({
    //     url: this.terrainUrl
    //   })
    // } else {
    //   this.scene.terrainProvider = null
    // }
  }

  // 获取地图类型
  getMapType () {
    return getMapType()
  }

  /**
   * @desc: 设置3d高程数据url
   * @param {String} url
   */
  setCesiumTerrainProvider (url = DEFALUT_3D_TERRAIN) {
    // http://192.168.30.21:7003/Home/TerrainTile/ // 远度
    // http://192.168.30.21:7003/Home/AwsTerrainTile/ // 亚马逊
    this.terrainUrl = url
  }

  /**
   * @desc: 设置地图模式（详情查看 mapMode.js）
   * @param {Number} mode 1-单线飞行 10-指点飞行
   * @param {Number} sessionId 当前飞机 sessionId 可选
   * @param {Number} mode 1-单线飞行 10-指点飞行
   */
  setMapMode (mode, sessionId, content = '') {
    setMapMode(mode, sessionId)

    this.oldMapModeMouseTipsContent = content
    this.mouseTipsContent = content
    console.log(this.mouseTipsContent)
  }

  getMapMode () { // 获取地图模式
    return getMapMode()
  }

  // --------------------- 地图控件相关（java原生） ---------------------

  /**
   * @desc: 通过经纬度跳转到指定位置
   * @param {array} latlng [lon, lat, h] h 可选
   * @param {number} zoom 缩放级别
   */
  locationToSomeWhereByLatlng (latlng, zoom) {
    let currentZoom = this.getZoom()
    zoom = !zoom ? currentZoom : zoom
    this.ol2d.locationToSomeWhereByLatlng(latlng, zoom)
    return latlng
  }

  /**
   * @desc: 一键查找无人机（以无人机为地图中心点）
   * @param {Number} sessionId
   * @param {Number} zoom 缩放级别，默认15
   */
  lookingForCurrentUav (sessionId, zoom = DEFAULT_ZOOM) {
    let currentUav = null
    if (sessionId) {
      currentUav = this.getUav(sessionId)
      setUavActiveSessionId(sessionId)
    } else {
      let currentSessionId = getUavActiveSessionId()
      currentUav = this.getUav(currentSessionId)
      setUavActiveSessionId(currentSessionId)
    }
    let latlng = currentUav.latlng
    this.locationToSomeWhereByLatlng(latlng, zoom)
    return getUavActiveSessionId()
  }

  /**
   * @desc: 跟随飞机
   * @param {Number} sessionId
   * @param {Number} isLocked
   */
  lockedCurrentUav (sessionId, isLocked) {
    if (sessionId) setUavActiveSessionId(sessionId)
    setUavLocked(isLocked)
    return isLocked
  }

  /**
   * @desc: 锁定吊舱视角
   * @param {Number} sessionId
   * @param {Number} isLocked
   */
  lockedCurrentPod (sessionId, isLocked) {
    if (sessionId) setUavActiveSessionId(sessionId)
    setPodLocked(isLocked)
    return isLocked
  }

  // --------------------- 鼠标操作提示相关，详情查看tip.js ---------------------

  /**
   * @desc: 切换地图模式改变鼠标提示
   * @param {Number} mapMode
   * @param {Array} coordinate
   */
  changeMouseTipsPosition (mapMode, coordinate, content) {
    // 常规模式和不可编辑模式除外
    if (mapMode == 0 || mapMode == 2) {
      if (this.mouseTips) this.mouseTips.delMouseTips()
      this.mouseTips = null
      return false
    }
    if (this.mouseTips == null) {
      this.mouseTips = new MouseTips(this.ol2d, this.mouseTipsMap)
      this.mouseTips.addMouseTips(mapMode, coordinate, this.mouseTipsContent)
    } else {
      this.mouseTips.changeMouseTipsPosition(coordinate, content)
    }
  }

  // --------------------- 无人机相关，详情查看uav.js ---------------------

  /**
   * @desc: 添加（渲染）2d，3d无人机
   * @param {string} options.sn 无人机sn
   * @param {string} options.sessionId 无人机sessionId
   * @param {number} options.type 无人机类型 0-固定翼 1-旋翼 2-编队旋翼
   * @param {object} options.element 2d无人机渲染挂载dom元素
   * @param {number} options.yaw 2d无人机飞行过程中航向角 0 - 360
   * @param {array} options.latlng 经纬度集合 [116.234, 39.345] or [116.234, 39.345, 100]
   * @param {array} options.hpr 偏转、俯仰、横滚集合 [180, 30, 0]
   * @param {String} options.popupData 信息弹窗
   * @use: this.addUav({sessionId: '', sn: 'HBRTEST202201061', type: 0, yaw: 0, latlng: [112.234, 35.345, 100], hpr: [90, 0, 0]})
   */
  addUav (options) {
    if (this.uavMap.has(options.sessionId)) return options.sessionId // 避免重复创建
    else {
      this.uav.addUav(options)
      return options.sessionId
    }
  }

  /**
   * @desc: 同步删除2d、2d无人机
   * @param {string} sessionId 无人机sn
   */
  delUav (sessionId) {
    this.uav.delUav(sessionId)
  }

  /**
   * @desc: 同步删除所有2d、3d无人机
   */
  delAllUavs () {
    this.uav.delAllUavs()
  }

  /**
   * @desc: 更新无人机自身信息（位置、航向、hpr等）其他实时信息（动态轨迹线、吊舱视野位置、下一目标点位置）在此处分发
   * @param {string} options.sn 无人机sn
   * @param {string} options.sessionId 无人机sessionId
   * @param {string} options.type 无人机类型
   * @param {number} options.num 无人机编号
   * @param {number} options.yaw 2d无人机飞行过程中航向角 0 - 360
   * @param {array} options.latlng 经纬度集合 [116.234, 39.345] or [116.234, 39.345, 100]
   * @param {array} options.hpr 偏转、俯仰、横滚集合 [180, 30, 0]
   * @param {array} options.podLatlng 吊舱视野中心点目标经纬度 [180, 30, 0]
   * @param {array} options.targetLatlng 下一个点位置 [180, 30, 0]
   * @param {string} options.airSpeed 空速
   * @param {string} options.groundSpeed 地速
   * @param {string} options.hspeed 垂直速度
   * @param {array} options.voltage 电压
   * @param {array} options.panelData 仪表盘数据
   * @param {String} options.popupData 信息弹窗
   * @use: this.updateUav({sn: 'HBRTEST202201061', yaw: 0, latlng: [112.234, 35.345, 100], hpr: [90, 0, 0]})
   */
  updateUav (options,isPlayback) { // 更新无人机相关信息
    this.uav.updateUav(options, isPlayback)
    let currentSessionId = getUavActiveSessionId()
    if (currentSessionId == options.sessionId) {
      this.updatePanelCallback(options.panelData)
    }
  }

  /**
   * @desc: 获取指定无人机
   * @param {Number} sessionId 无人机sessionId
   * @return {Number} sessionId
   */
  getUav (sessionId) {
    return this.uav.getUav(sessionId)
  }

  /**
   * @desc: 获取当前选中的无人机 sessionId
   * @return {Number} sessionId
   */
  getUavActiveSessionId () {
    return getUavActiveSessionId()
  }

  /**
   * @desc: 切换当前选中的无人机（切换飞机控制权）
   * @param {Number} sessionId
   * @return {Number} sessionId
   */
  changeCurrentUav (sessionId) {
    setUavActiveSessionId(sessionId)
    return sessionId
  }

  // --------------------- 起飞点 ---------------------
  /**
   * @desc: 添加起飞点(olcs rend3d = true 同步处理，暂不添加3dentity)
   * @param {Number} sessionId
   * @param {Array} latlng
   */
  addTakeOffPoint (sessionId, latlng) {
    this.takeOffPointInstance.addTakeOffPoint(sessionId, latlng)
  }

  /**
   * @desc: 更新起飞点
   * @param {Number} sessionId
   * @param {Array} latlng
   */
  updateTakeOffPointPosition (sessionId, latlng) {
    this.takeOffPointInstance.updateTakeOffPointPosition(sessionId, latlng)
  }

  /**
   * @desc: 删除起飞点
   * @param {Number} sessionId
   */
  delTakeOffPoint (sessionId) {
    this.takeOffPointInstance.delTakeOffPoint(sessionId)
  }

  /**
   * @desc: 删除所有起飞点
   */
  delAllTakeOffPoint () {
    this.takeOffPointInstance.delAllTakeOffPoint()
  }

  // --------------------- 指点飞行 ---------------------

  /**
   * @desc: 指点飞行模式在飞行之前打的点（有且只有一个，第二次点击替换上一次）
   * @param {Number} sessionId
   * @param {Array} latlng
   * @param {String} image
   * @param {Number} radius
   * @param {Object} style
   */
  addGivePointToFly (sessionId, latlng, image, radius, style) {
    this.givePointFlyInstance.addGivePointToFly(sessionId, latlng, image, radius, style)
  }

  /**
   * @desc: 更新指点飞行的点
   * @param {Number} sessionId
   * @param {Array} latlng
   */
  updateGivePointPosition (sessionId, latlng, radius, style) {
    this.givePointFlyInstance.updateGivePointPosition(sessionId, latlng, radius, style)
  }

  /**
   * @desc: 删除指点飞行的点
   * @param {Number} sessionId
   */
  delGivePointToFly (sessionId) {
    this.givePointFlyInstance.delGivePointToFly(sessionId)
  }

  /**
   * @desc: 删除所有指点飞行的点
   */
  delAllGivePoint () {
    this.givePointFlyInstance.delAllGivePoint()
  }

  // --------------------- 自定义点相关（火点、基站等） ---------------------
  /**
   * @desc: 添加自定义点
   * @param {Number} sessionId
   * @param {Array} latlng
   * @param {Number} type 自定义点类型 1-精确着火点 2-粗略着火点 3-警察位置 4-消防员位置 5-漫游基站位置
   * @param {String} image
   * @param {Number} radius
   * @param {Number} isEdit
   * @param {Object} style circle 样式
   * @return {*}
   * @use: 使用示例
   */
  addCustomPoint (sessionId, latlng, type, image, radius, isEdit, style, showOverlay, popupData, offset) {
    this.customPointInstance.addCustomPoint(sessionId, latlng, type, image, radius, isEdit, style, showOverlay, popupData, offset)
  }

  /**
   * @desc: 修改自定义点
   * @param {Number} sessionId
   * @param {Array} sessionId
   * @param {Number} type
   * @param {String} image
   * @param {Number} radius
   * @param {Number} isEdit
   * @param {Object} style
   */
  updateCustomPoint (sessionId, latlng, radius, style, showOverlay, popupData, showTrack) {
    this.customPointInstance.updateCustomPoint(sessionId, latlng, radius, style, showOverlay, popupData, showTrack)
  }

  /**
   * @desc: 删除自定义点
   * @param {Number} sessionId
   */
  delCustomPoint (sessionId) {
    this.customPointInstance.delCustomPoint(sessionId)
  }

  updateAirGroundCooAircrew (sessionId, latlng, yaw, hpr, showTrack, type) {
    this.customPointInstance.updateAirGroundCooAircrew(sessionId, latlng, yaw, hpr, showTrack, type)
  }

  /**
   * @desc: 删除自定义点中的空地协同机组相关
   * @param {Number} sessionId
   */
  delAirGroundCooAircrew (sessionId) {
    this.customPointInstance.delAirGroundCooAircrew(sessionId)
  }

  /**
   * @desc: 获取自定义点信息
   * @param {Number} sessionId
   */
  getCustomPoint (sessionId) {
    return this.customPointInstance.getCustomPoint(sessionId)
  }

  // --------------------- 仿真相关 ---------------------
  /**
   * @desc: 添加仿真起飞点(olcs rend3d = true 同步处理，暂不添加3dentity)
   * @param {Number} sessionId
   * @param {Array} latlng
   */
  addSimulationTakeOffPoint (sessionId, latlng, image) {
    this.simulationInstance.addTakeOffPoint(sessionId, latlng, image)
  }

  /**
   * @desc: 更新起飞点
   * @param {Number} sessionId
   * @param {Array} latlng
   */
  updateSimulationTakeOffPointPosition (sessionId, latlng) {
    this.simulationInstance.updateTakeOffPointPosition(sessionId, latlng)
  }

  /**
   * @desc: 删除起飞点
   * @param {Number} sessionId
   */
  delSimulationTakeOffPoint (sessionId) {
    this.simulationInstance.delTakeOffPoint(sessionId)
  }

  /**
   * @desc: 删除所有起飞点
   */
  delAllSimulationTakeOffPoint () {
    this.simulationInstance.delAllTakeOffPoint()
  }

  /**
   * @desc: 功能描述
   * @param {*} sessionId
   * @param {*} latlng
   * @param {*} width
   * @param {*} height
   * @param {*} image
   * @return {*}
   * @use: 使用示例
   */
  addSimulationArea (sessionId, latlng, width, height, image) {
    this.simulationInstance.addAreaCenterPoint(sessionId, latlng, width, height, image)
  }

  renderSimulationArea (sessionId, latlng, width, height, image) {
    this.simulationInstance.addAreaCenterPoint(sessionId, latlng, width, height, image)
  }

  delSimulationArea (sessionId) {
    this.simulationInstance.delAreaCenterPoint(sessionId)
  }

  // --------------------- 航线/点相关 ---------------------

  /**
   * @desc: 单点飞行打点连线（可扩展为通用添加航线方法） addAirLine() ==> 最新
   * @param {Number} sessionId 线sessionId
   * @param {*} lineType 航线类型 1-单线飞行 2-区域巡检 3-带状航线
   * @param {*} pointList 航点数组 [{id, pointType 0-起始点 1-任务航点 2-降高 3-刹车 4-降落, num 编号, numPrefix S-起始点 L-降落点, latlng, isHover 是否盘旋（决定是否渲染radius）, radius, speed, ponitStyle }]，第一次传值包含降落航线共4个点，后续一个点，均在list中
   * @param {*} lineStyle 线样式（弃用）
   * @return {*}
   * @use: 使用示例
   */
  addSingleLineToFly (sessionId, lineType, pointList, lineStyle) {
    if (lineType == 1) { // 单线飞行
      if (this.airlinePointMap.get(sessionId) == undefined) { // 第一次点击的时候创建 hash Map
        this.airlinePointMap.set(sessionId, { 'type': lineType, 'taskPoint': [], 'landPoint': [] })

        let currentPointMap = this.airlinePointMap.get(sessionId)
        let currentTaskPointList = currentPointMap.taskPoint
        let currentLandPointList = currentPointMap.landPoint

        // 二者同时为空，第一次打点。存在删除操作过程中删除的只剩任务点或者只剩降落点
        if (currentTaskPointList.length <= 0 && currentLandPointList.length <= 0) {
          // 添加S点
          let taskPoints = []
          if (pointList.length > 1) {
            this.hasLandLine = true
            taskPoints = pointList.slice(0, pointList.length - 3)
          } else taskPoints = pointList.slice(0, 1) // 旋翼机，没有降落航线

          taskPoints.forEach(item => {
            this.addAirinePoint(item.id, item.latlng, item.pointType, item.num, item.numPrefix || '', item.isHover, item.radius || 160, 1, item.pointStyle, sessionId, lineType, currentTaskPointList)
          })

          // 添加降落点
          if (!this.hasLandLine) return false // 旋翼机，没有降落航线
          let landPoints = pointList.slice(pointList.length - 3, pointList.length)
          landPoints.forEach(item => {
            this.addAirinePoint(item.id, item.latlng, item.pointType, item.num, item.numPrefix || '', item.isHover, item.radius, 1, item.pointStyle, sessionId, lineType, currentLandPointList)
          })
        }
      } else {
        let currentLineMap = this.airlinePointMap.get(sessionId)
        let currentTaskPointList = currentLineMap.taskPoint
        // 添加点
        this.addAirinePoint(pointList[0].id, pointList[0].latlng, pointList[0].pointType, pointList[0].num, pointList[0].numPrefix || '', pointList[0].isHover, pointList[0].radius, 1, pointList[0].pointStyle, sessionId, lineType, currentTaskPointList)
      }
    }
  }

  /**
   * @desc: 点击地图添加点 ==> 最新
   * @param {Number} id 点id
   * @param {Array} latlng
   * @param {Number} type 点类型 0-起始点 1-任务航点 2-降高 3-刹车 4-降落
   * @param {Number} num 点编号（索引）
   * @param {String} prefix 点编号前缀（索引）
   * @param {Number} isHover 是否盘旋 根据此参数来决定是否绘制盘旋半径
   * @param {Number} radius 盘旋半径
   * @param {Number} isEdit 是否可编辑
   * @param {Object} style 自定义样式
   * @param {Number} parentId hash Map sessionId
   * @param {Number} parentType hash Map type
   * @param {Array} pointList 传入的点数组
   * @return {*}
   * @use: 使用示例
   */
  addAirinePoint (id, latlng, type = 1, num, prefix = '', isHover, radius, isEdit, style, parentId, parentType, pointList) {
    // 2d点
    let point = new Point(this.ol2d, this.pointSource)
    point.addPoint(id, latlng, type, num, prefix, isHover, radius, isEdit, style, parentId, parentType)
    pointList.push(point)
    // console.log(pointList)

    // 3D点
    let color3d = style.fillColor || 'rgba(0, 153, 255, 1)'
    let label3d = prefix + String(num)
    let position = new Cesium.Cartesian3.fromDegrees(latlng[0], latlng[1], latlng[2] || 80)
    let point3d = new Point3d(this.entities, 'airPoint3d_' + id, type, color3d, label3d)
    point3d.position = position

    // circle
    if (isHover && radius > 0) {
      let circleFillColor = color2Rgba(style.fillColor)
      let circleStyle = { ...style }
      circleStyle.fillColor = circleFillColor
      // 2d
      let circle = new Circle(this.ol2d, this.circleSource)
      circle.addCircle(id, 1, latlng, isHover, radius, circleStyle, false)
      // 3d
      let circle3d = new Circle3d(this.entities, 'airCircle3d_' + id, 1, latlng, radius, circleStyle, false)
      circle3d.position = position

      this.airlineCircleMap.set(id, { 'item2d': circle, 'item3d': circle3d })
    }

    // 任务线
    let taskPointList = this.airlinePointMap.get(parentId).taskPoint
    // 两个任务点能够连成线
    if (taskPointList.length > 1 && taskPointList.length <= 2 && type <= 1) {
      if (this.airlineTaskLineMap.get(parentId) == undefined) {
        // 2d
        let firstPointLatlng = taskPointList[0].feature.getProperties()['latlng']
        let secondPointLatlng = taskPointList[1].feature.getProperties()['latlng']
        let taskLine = new Line(this.ol2d, this.ol3d, this.lineSource)
        taskLine.addLine(parentId, [firstPointLatlng, secondPointLatlng], 1, isEdit, style)
        // 3d
        let positions = []
        positions[0] = new Cesium.Cartesian3.fromDegrees(firstPointLatlng[0], firstPointLatlng[1], firstPointLatlng[2] || 80)
        positions[1] = new Cesium.Cartesian3.fromDegrees(secondPointLatlng[0], secondPointLatlng[1], secondPointLatlng[2] || 80)
        let taskLine3d = new Line3d(this.entities, 'airLine3dTask_' + parentId, style, false)
        taskLine3d.positions = positions

        this.airlineTaskLineMap.set(parentId, { 'type': parentType, 'item2d': taskLine, 'item3d': taskLine3d })
        // 第一条线就开始存储 任务LineString 插入点时使用
        this.airlineTaskLineString = this.airlineTaskLineMap.get(parentId).item2d.singleAirLine
      }
    }
    // 三个任务点及以上
    if (taskPointList.length > 2 && type <= 1) {
      // 此处是 LineString 对象
      let singleTaskLine = this.airlineTaskLineMap.get(parentId).item2d.singleAirLine
      let coordinate = fromLonLat(latlng)
      singleTaskLine.appendCoordinate(coordinate)
      // 3d （线特有的更新方式）
      let singleTaskLine3d = this.airlineTaskLineMap.get(parentId).item3d
      let positions = singleTaskLine3d.positions
      positions.push(new Cesium.Cartesian3.fromDegrees(latlng[0], latlng[1], latlng[2] || 80))
    }

    if (!this.hasLandLine) return false
    // 降落线
    let landPointList = this.airlinePointMap.get(parentId).landPoint
    // 两个降落点能够连成线
    if (landPointList.length > 1 && landPointList.length <= 2 && type >= 2) {
      if (this.airlineLandLineMap.get(parentId) == undefined) {
        // 2d
        let firstPointLatlng = landPointList[0].feature.getProperties()['latlng']
        let secondPointLatlng = landPointList[1].feature.getProperties()['latlng']
        let landLine = new Line(this.ol2d, this.ol3d, this.lineSource)
        landLine.addLine(parentId, [firstPointLatlng, secondPointLatlng], 2, isEdit, style)
        // 3d
        let positions = []
        positions[0] = new Cesium.Cartesian3.fromDegrees(firstPointLatlng[0], firstPointLatlng[1], firstPointLatlng[2] || 80)
        positions[1] = new Cesium.Cartesian3.fromDegrees(secondPointLatlng[0], secondPointLatlng[1], secondPointLatlng[2] || 80)
        let landLine3d = new Line3d(this.entities, 'airLine3dLand_' + parentId, style, false)
        landLine3d.positions = positions

        this.airlineLandLineMap.set(parentId, { 'type': parentType, 'item2d': landLine, 'item3d': landLine3d })
      }
    }
    // 三个降落点及以上
    if (landPointList.length > 2 && type >= 2) {
      // 此处是 LineString 对象
      let singleLandLine = this.airlineLandLineMap.get(parentId).item2d.singleAirLine
      let coordinate = fromLonLat(latlng)
      singleLandLine.appendCoordinate(coordinate)
      // 3d （线特有的更新方式）
      let singleLandLine3d = this.airlineLandLineMap.get(parentId).item3d
      let positions = singleLandLine3d.positions
      positions.push(new Cesium.Cartesian3.fromDegrees(latlng[0], latlng[1], latlng[2] || 80))
    }

    // 添加引导线
    if (taskPointList.length > 0 && landPointList.length >= 3) {
      let lastTaskPoint = taskPointList[taskPointList.length - 1]
      let firstLandPoint = landPointList[0]
      let lastLandPoint = landPointList[2]
      this.setLastTaskPointToFirstLandPointGuideDashLine(lastTaskPoint, firstLandPoint, style)
      this.setFirstLandPointToSecondLandPointGuideLine(firstLandPoint, lastLandPoint, style)
    }
  }

  /**
   * @desc: 点击线插入点
   * @param {Number} id 当前点id
   * @param {Array} latlng
   * @param {Number} isHover
   * @param {Number} radius
   * @param {Number} isEdit
   * @param {Object} style
   * @param {Number} parentId hash Map sessionId
   * @param {Number} parentType hash Map type
   * @return {*}
   * @use: 使用示例
   */
  insertPoint (id, latlng, isHover, radius = 0, isEdit = 1, style, parentId, parentType) {
    if (!style || style == null) style = {}

    // let latlng = toLonLat(coordinate)
    let coordinate = fromLonLat(latlng)
    let oldcoordinates = this.airlineTaskLineString.getCoordinates()
    var insertIndex = -1
    // 计算插入的点分割为两条线相加的长度是否等于被分割的线长度，得出insertIndex
    for (var i = 1; i < oldcoordinates.length; i++) {
      var lLine = new LineString([oldcoordinates[i], oldcoordinates[i - 1]])
      var sLine = new LineString([oldcoordinates[i - 1], coordinate])
      var eLine = new LineString([coordinate, oldcoordinates[i]])
      // console.log(Math.round(getLength(sLine)), Math.round(getLength(eLine)), Math.round(getLength(lLine)))
      if (Math.round(getLength(sLine) + getLength(eLine)) == Math.round(getLength(lLine))) {
        insertIndex = i
        break
      }
    }
    // console.log(insertIndex)
    if (insertIndex >= 0) {
      // 2d点
      let point = new Point(this.ol2d, this.pointSource)
      point.addPoint(id, latlng, 1, insertIndex + 1, '', isHover, radius, isEdit, style, parentId, parentType)
      let taskPointList = this.airlinePointMap.get(parentId).taskPoint
      taskPointList.splice(insertIndex, 0, point)
      // 3D点
      let color3d = 'rgba(0, 153, 255, 1)'
      let label3d = String(insertIndex + 1)
      let position = new Cesium.Cartesian3.fromDegrees(latlng[0], latlng[1], latlng[2] || 80)
      let point3d = new Point3d(this.entities, 'airPoint3d_' + id, 1, color3d, label3d)
      point3d.position = position

      // circle
      if (isHover && radius > 0) {
        // 2d
        let circle = new Circle(this.ol2d, this.circleSource)
        circle.addCircle(id, 1, latlng, isHover, radius, style, false)
        // 3d
        let circle3d = new Circle3d(this.entities, 'airCircle3d_' + id, 1, latlng, radius, style, false)
        circle3d.position = position

        this.airlineCircleMap.set(id, { 'item2d': circle, 'item3d': circle3d })
      }

      // 2d线
      oldcoordinates.splice(insertIndex, 0, coordinate)
      this.airlineTaskLineString.setCoordinates(oldcoordinates)
      // 3d线
      let linePosition = new Cesium.Cartesian3.fromDegrees(latlng[0], latlng[1], latlng[2] || 80)
      let singleTaskLine3d = this.airlineTaskLineMap.get(parentId).item3d
      singleTaskLine3d.positions.splice(insertIndex, 0, linePosition)

      // 点添加成功之后
      let slicePointList = taskPointList.slice(insertIndex + 1, taskPointList.length)
      this.resetAfterCurrentPointProperties(slicePointList, 2)

      this.idArr.splice(insertIndex, 0, id)
    }
  }

  /**
   * @desc: 拖动点改变位置同步改变相关Feature ==> 最新
   * @param {Number} id
   * @param {Array} latlng
   * @param {*} type 点类型 0-起始点 1-任务航点 2-降高 3-刹车 4-降落
   * @param {*} index 点编号（索引）
   * @param {*} isHover
   * @param {Number} radius
   * @param {*} parentId 父级id
   * @param {*} parentType 父级类型 1-单线飞行可编辑
   * @return {*}
   * @use: 使用示例
   */
  changePointPosition (id, latlng, type, index, isHover, radius, parentId, parentType) {
    let currentairlinePointMap = null
    let currentairlineTaskLineMap = null
    let currentairlineLandLineMap = null
    if (parentType == 1 || parentType == 100) { // 1-单线飞行或者机载航线均可编辑
      currentairlinePointMap = this.airlinePointMap.get(parentId)
      currentairlineTaskLineMap = this.airlineTaskLineMap.get(parentId)
      currentairlineLandLineMap = this.airlineLandLineMap.get(parentId)

      let taskPointList = currentairlinePointMap.taskPoint
      let landPointList = currentairlinePointMap.landPoint
      if (type < 2) { // 任务点（包含S点）
        // 改变点
        if (taskPointList.length >= 2) {
          let currentTaskLineGeom = currentairlineTaskLineMap.item2d
          let currentTaskLine3d = currentairlineTaskLineMap.item3d
          this.changePointPositionChildFn(taskPointList, latlng, currentTaskLineGeom, index, currentTaskLine3d, parentId)
        } else {
          this.changePointPositionChildFn(taskPointList, latlng, null, index, null, parentId)
        }
      }
      if (!this.hasLandLine) return false
      if (type >= 2) { // 降落点
        let currentLandLingGeom = currentairlineLandLineMap.item2d
        let currentLandLine3d = currentairlineLandLineMap.item3d
        // 改变点
        this.changePointPositionChildFn(landPointList, latlng, currentLandLingGeom, index, currentLandLine3d, parentId)
      }
    }
  }

  /**
   * @desc: 点位置发生改变子方法 ==> 最新
   * @param {Object} currentPointGeom
   * @param {Array} latlng
   * @return {*}
   * @use: 使用示例
   */
  changePointPositionChildFn (pointList, latlng, currentLineGeom, index, currentLine3d, parentId) {
    // 2d点
    let coordinate = fromLonLat(latlng)
    let currentPointGeom = pointList[index]
    let feature = currentPointGeom.feature
    feature.getGeometry().setCoordinates(coordinate)
    // 3d点
    let id = feature.getProperties()['id']
    let currentEntity = this.entities.getById('airPoint3d_' + id)
    if (currentEntity) currentEntity.position = new Cesium.Cartesian3.fromDegrees(latlng[0], latlng[1], latlng[2] || 80)
    // circle
    let isHover = feature.getProperties()['isHover']
    let radius = feature.getProperties()['radius']
    if (isHover && radius > 0) {
      let currentSingleLineToFlyCircleGeom = this.airlineCircleMap.get(id).item2d
      let feature = currentSingleLineToFlyCircleGeom.feature
      feature.getGeometry().setCenter(coordinate)
    }
    // 排除只有S点情况，此时拖动更新引导线
    if (currentLineGeom != null && currentLine3d != null) {
      // 2d线
      let singleTaskLine = currentLineGeom.singleAirLine
      let coordinatesArr = singleTaskLine.getCoordinates()
      coordinatesArr[index] = coordinate
      singleTaskLine.setCoordinates(coordinatesArr)
      // 3d线
      let positions = currentLine3d.positions
      positions[index] = new Cesium.Cartesian3.fromDegrees(latlng[0], latlng[1], latlng[2] || 80)
    }

    if (!this.hasLandLine) return false
    // 引导线
    let type = currentPointGeom.feature.getProperties()['type']
    let taskPointList = this.airlinePointMap.get(parentId).taskPoint
    let landPointList = this.airlinePointMap.get(parentId).landPoint
    if ((index == 0 || index == taskPointList.length - 1) && type < 2) { // 任务最后一个点
      let lastTaskPoint = taskPointList[taskPointList.length - 1]
      let firstLandPoint = landPointList[0]
      this.setLastTaskPointToFirstLandPointGuideDashLine(lastTaskPoint, firstLandPoint)
    }
    if (index == 0 && type >= 2) { // 降落航点L1
      let lastTaskPoint = taskPointList[taskPointList.length - 1]
      let firstLandPoint = landPointList[0]
      let lastLandPoint = landPointList[2]
      this.setLastTaskPointToFirstLandPointGuideDashLine(lastTaskPoint, firstLandPoint)
      this.setFirstLandPointToSecondLandPointGuideLine(firstLandPoint, lastLandPoint)
    }
    if (index == landPointList.length - 1 && type >= 2) { // 降落点L3
      let firstLandPoint = landPointList[0]
      let lastLandPoint = landPointList[2]
      this.setFirstLandPointToSecondLandPointGuideLine(firstLandPoint, lastLandPoint)
    }
  }

  /**
   * @desc: 删除航线中的航点（同时删除线、circle等） ==> 最新
   * @param {Number} sessionId 航线sessionId
   * @param {Number} lineType 航线类型 1-单线飞行
   * @param {Number} pointId 点id
   * @param {Number} pointType 点type 0-起始点 1-任务航点 2-降高 3-刹车 4-降落
   * @param {Number} index 点编号（索引）
   * @return {*}
   * @use: 使用示例
   */
  delAirLinePoint (sessionId, lineType, pointId, pointType, index) {
    if (lineType == 1 || lineType == 100) { // 单线飞行中的点
      // 非任务点禁止删除
      if (pointType <= 0 || pointType > 1) return false

      let currentairlinePointMap = this.airlinePointMap.get(sessionId)
      let currentairlineTaskLineMap = this.airlineTaskLineMap.get(sessionId)
      // 删除任务点2d
      let taskPointList = currentairlinePointMap.taskPoint
      let currentTaskPoint = taskPointList[index]
      this.pointSource.removeFeature(currentTaskPoint.feature)
      taskPointList.splice(index, 1)
      // 删除任务点3d
      this.entities.removeById('airPoint3d_' + pointId) // 3d 根据id删除所有对应entity

      // circle
      let isHover = currentTaskPoint.feature.getProperties()['isHover']
      let radius = currentTaskPoint.feature.getProperties()['radius']
      if (isHover && radius > 0) {
        // 2d
        let currentCircle = this.airlineCircleMap.get(pointId)
        this.circleSource.removeFeature(currentCircle.item2d.feature)
        // 3d
        // this.entities.removeById('airCircle3d_' + pointId)
        this.airlineCircleMap.delete(pointId)
      }

      // 删除任务线2d
      let currentTaskLineGeom = currentairlineTaskLineMap.item2d
      let singleTaskLine = currentTaskLineGeom.singleAirLine
      let coordinates = singleTaskLine.getCoordinates()
      coordinates.splice(index, 1)
      singleTaskLine.setCoordinates(coordinates)
      // 删除任务线3d
      let currentTaskLine3d = currentairlineTaskLineMap.item3d
      currentTaskLine3d.positions.splice(index, 1)

      if (this.hasLandLine) { // 非旋翼，有降落航线
        // 修改引导线（删除最后一个任务点）
        if (index == taskPointList.length) { // 点已经删除后的长度
          let lastTaskPoint = taskPointList[taskPointList.length - 1]
          let firstLandPoint = currentairlinePointMap.landPoint[0]
          this.setLastTaskPointToFirstLandPointGuideDashLine(lastTaskPoint, firstLandPoint)
        }
      }

      // 更新当前点之后的所有点属性
      let slicePointList = taskPointList.slice(index, taskPointList.length)
      this.resetAfterCurrentPointProperties(slicePointList, 1)

      this.idArr.splice(index, 1) // 保证删除之后模拟打点继续循环
    }
  }

  /**
   * @desc: 批量删除点
   * @param {Number} sessionId
   * @param {Number} lineType
   * @param {Number} pointType
   * @param {String} arr 需要被删除点索引数组  [1,3,5,8]
   * @return {*}
   * @use: 使用示例
   */
  batchDelAirLinePoint (sessionId, lineType, pointType, arr) {
    if (lineType == 1 || lineType == 100) { // 单线飞行中的点
      if (pointType <= 0 || pointType > 1) return false // 非任务点禁止删除

      let newArr1 = [...new Set(arr)] // es6 去重
      let newArr2 = newArr1.sort((a, b) => { return a - b }) // 倒序
      let indexArr = newArr2.reverse()

      let currentAirlinePointMap = this.airlinePointMap.get(sessionId)
      let currentAirlineTaskLineMap = this.airlineTaskLineMap.get(sessionId)
      let taskPointList = currentAirlinePointMap.taskPoint
      let taskPointListLength = taskPointList.length // 删除前的任务点数组长度

      for (var i = 0; i <= indexArr.length - 1; i++) {
        let index = indexArr[i]
        let currentTaskPoint = taskPointList[index]
        this.pointSource.removeFeature(currentTaskPoint.feature)
        taskPointList.splice(index, 1)
        // 删除任务点3d
        let pointId = currentTaskPoint.feature.getProperties()['id']
        this.entities.removeById('airPoint3d_' + pointId) // 3d 根据id删除所有对应entity

        // circle
        let isHover = currentTaskPoint.feature.getProperties()['isHover']
        let radius = currentTaskPoint.feature.getProperties()['radius']
        if (isHover && radius > 0) {
          // 2d
          let currentCircle = this.airlineCircleMap.get(pointId)
          this.circleSource.removeFeature(currentCircle.item2d.feature)
          // 3d
          // this.entities.removeById('airCircle3d_' + pointId)
          this.airlineCircleMap.delete(pointId)
        }

        // 删除任务线2d
        let currentTaskLineGeom = currentAirlineTaskLineMap.item2d
        let singleTaskLine = currentTaskLineGeom.singleAirLine
        let coordinates = singleTaskLine.getCoordinates()
        coordinates.splice(index, 1)

        // 解决部分浏览器3d情况下必须两个position的情况，否则报错
        let len = this.airlinePointMap.get(sessionId).taskPoint.length
        if (index == 1 && len == 1) singleTaskLine.setCoordinates([coordinates[0], coordinates[0]])
        else singleTaskLine.setCoordinates(coordinates)
        // 删除任务线3d
        let currentTaskLine3d = currentAirlineTaskLineMap.item3d
        // 解决部分浏览器3d情况下必须两个position的情况，否则报错
        // if (index == 1 && len == 1) currentTaskLine3d.positions[1] = currentTaskLine3d.positions[0]
        // else currentTaskLine3d.positions.splice(index, 1)
        currentTaskLine3d.positions.splice(index, 1)

        this.idArr.splice(index, 1) // 保证删除之后模拟打点继续循环
      }

      let minIndex = indexArr[indexArr.length - 1]
      let slicePointList = taskPointList.slice(minIndex, taskPointList.length) // slice 不改变原数组，截取最小索引点之后的所有点
      slicePointList.forEach((item, index) => {
        let properties = item.feature.getProperties()
        let prefix = properties.prefix
        // 修改2d feature属性
        properties.index = index + minIndex
        properties.style.text = prefix + String(index + minIndex + 1)
        item.feature.setProperties(properties)
        item.feature.setStyle(setOlStylePoint(properties.style))

        // 修改3d点属性
        let id = properties.id
        let currentEntity = this.entities.getById('airPoint3d_' + id)
        if (currentEntity) currentEntity.billboard.image = new Cesium.PinBuilder().fromText(prefix + String(index + minIndex + 1), Cesium.Color.fromCssColorString(properties.style.fillColor), 35).toDataURL()
      })

      if (this.hasLandLine) { // 非旋翼，有降落航线
        // 删除的点包含最后一个任务点时或者任务点删的只剩下S1点
        if (indexArr[0] == taskPointListLength - 1 || indexArr.length == taskPointListLength - 1) { // 点删除前的长度
          let lastTaskPoint = taskPointList[taskPointList.length - 1]
          let firstLandPoint = currentAirlinePointMap.landPoint[0]
          this.setLastTaskPointToFirstLandPointGuideDashLine(lastTaskPoint, firstLandPoint)
        }
      }

      // 删除只剩下一个S1点时，需要删除对应3d线，删除线hash Map，保证再次执行打点操作，addPoint方法能够正常运行
      if (indexArr.length == taskPointListLength - 1) {
        this.entities.removeById('airLine3dTask_' + sessionId)
        this.airlineTaskLineMap.delete(sessionId)
      }
    }
  }

  /**
  * @desc: 删除或者插入某一个点，修改后面的所有点 ==> 最新
  * @param {Arr} sliceArr 截取的某一个点之后的所有点
  * @param {Number} index 当前点索引（删除点的索引、插入点的索引）
  * @param {Number} type 1-删除 2-插入
  * @return {*}
  * @use: 使用示例
  */
  resetAfterCurrentPointProperties (slicePointList, type) {
    let step = -1 // 默认删除操作
    if (type == 2) step = 1 // 插入操作
    slicePointList.forEach(item => {
      let properties = item.feature.getProperties()
      let oldIndex = properties.index // 每个点原始index
      let prefix = properties.prefix
      // 修改2d feature属性
      properties.index = oldIndex + step
      properties.style.text = prefix + String(oldIndex + step + 1)
      item.feature.setProperties(properties)
      item.feature.setStyle(setOlStylePoint(properties.style))

      // 修改3d点属性
      let id = properties.id
      let currentEntity = this.entities.getById('airPoint3d_' + id)
      if (currentEntity) currentEntity.billboard.image = new Cesium.PinBuilder().fromText(prefix + String(oldIndex + step + 1), Cesium.Color.fromCssColorString(properties.style.fillColor), 35).toDataURL()
    })
  }

  /**
   * @desc: 降落航点L1和任务航线最后一个航点之间的引导线 ==> 最新
   * @param {Object} lastTaskPoint 航点半径
   * @param {Object} firstLandPoint 航点半径
   * @return {*}
   * @use: 使用示例
   */
  setLastTaskPointToFirstLandPointGuideDashLine (lastTaskPoint, firstLandPoint, style) {
    let landL1Coordinate = firstLandPoint.feature.getGeometry().getCoordinates() // 获取降落航点L1实际 coordinate
    let taskLastPointCoordinate = lastTaskPoint.feature.getGeometry().getCoordinates() // 获取任务航点最后一个点实际 coordinate
    let landL1Point = toLonLat(landL1Coordinate) // 降落航点L1
    let taskLastPoint = toLonLat(taskLastPointCoordinate) // 任务航点最后一个点
    let radius = this.getFeatureProperty(firstLandPoint, 'radius') || 160

    let rate = 0
    let dis = getDistance(taskLastPoint, landL1Point) // 两点之间距离

    let zhuanWanPoint = [0, 0, taskLastPoint[2]] // 任务航点最后一个点飞向降落航点L1的转弯点
    if (dis > 2 * radius) {
      rate = (2 * radius - 50) / dis
      zhuanWanPoint[0] = (taskLastPoint[0] - landL1Point[0]) * rate + landL1Point[0]
      zhuanWanPoint[1] = (taskLastPoint[1] - landL1Point[1]) * rate + landL1Point[1]
    }
    if ((dis * (1 - rate) < 80) || dis <= 2 * radius) {
      rate = 80 / dis
      zhuanWanPoint[1] = (landL1Point[1] - taskLastPoint[1]) * rate + taskLastPoint[1]
      zhuanWanPoint[0] = (landL1Point[0] - taskLastPoint[0]) * rate + taskLastPoint[0]
    }

    let angle = this.getAngleOf2Point(false, landL1Point, taskLastPoint)
    let qieRad = angle - Math.PI / 3 // 弧度
    let qieXianPoint = [0, 0, landL1Point[2]] // 和降落航点L1相切的点
    qieXianPoint[0] = landL1Point[0] + radius * Math.sin(qieRad) / 1000 / (EARTH_RADIUS * Math.cos(landL1Point[1] / 180 * Math.PI)) * 180 / Math.PI
    qieXianPoint[1] = landL1Point[1] + radius * Math.cos(qieRad) / 1000 / EARTH_RADIUS * 180 / Math.PI

    let guidePointsArr = []
    guidePointsArr.push(taskLastPoint)
    guidePointsArr.push(zhuanWanPoint)
    guidePointsArr.push(qieXianPoint)
    // console.log(guidePointsArr)

    // 添加引导线
    // 任务航线最后一个点到L1，取L1点的id作为 airlineGuideLineMap 的sessionId
    let sessionId = this.getFeatureProperty(firstLandPoint, 'id')
    let currentGuideLine = this.airlineGuideLineMap.get(sessionId)
    if (!currentGuideLine) {
      let guideLine = new Line(this.ol2d, this.ol3d, this.lineSource)
      guideLine.addGuideLine(sessionId, guidePointsArr, style)
      // 3d
      let positions = []
      guidePointsArr.forEach((item, index) => {
        positions[index] = new Cesium.Cartesian3.fromDegrees(item[0], item[1], item[2] || 100)
      })
      let guideLine3d = new Line3d(this.entities, 'guideLine_' + sessionId, style, true, false)
      guideLine3d.positions = positions

      this.airlineGuideLineMap.set(sessionId, { 'item2d': guideLine, 'item3d': guideLine3d })
    } else {
      let coordinates = []
      let positions = []
      guidePointsArr.forEach(item => {
        coordinates.push(fromLonLat(item))
        positions.push(new Cesium.Cartesian3.fromDegrees(item[0], item[1], item[2] || 100))
      })
      currentGuideLine.item2d.guideLine.setCoordinates(coordinates)
      currentGuideLine.item3d.positions = positions
    }
  }

  /**
   * @desc: 降落航点L1绕圈之后飞向L3的引导线 ==> 最新
   * @param {Number} radius 航点半径
   * @return {*}
   * @use: 使用示例
   */
  setFirstLandPointToSecondLandPointGuideLine (firstLandPoint, lastLandPoint, style) {
    let landL1Coordinate = firstLandPoint.feature.getGeometry().getCoordinates() // 获取降落航点L1实际 coordinate
    let landL3PointCoordinate = lastLandPoint.feature.getGeometry().getCoordinates() // 获取任务航点最后一个点实际 coordinate

    var landL1Point = toLonLat(landL1Coordinate) // L1
    var landL3Point = toLonLat(landL3PointCoordinate) // L3
    var angle = this.getAngleOf2Point(false, landL3Point, landL1Point)
    let radius = this.getFeatureProperty(firstLandPoint, 'radius') || 160

    var disArray = [98, 95, 90, 82, 73, 68, 62, 56, 53, 38, 24, 21, 17, 22, 38]
    var angArray = [-5, 0, 5, 13, 23, 28, 33, 38, 40, 50, 68, 75, 90, 165, 180]

    var guidePointsArr = []
    for (var i = 0; i < 15; i++) {
      var tempRadius = radius * disArray[i] / 100
      var tempAngle = angle - angArray[i] * Math.PI / 180
      var tempPoint = [0, 0, landL1Point[2]]
      tempPoint[0] = landL1Point[0] + tempRadius * Math.sin(tempAngle) / 1000 / (EARTH_RADIUS * Math.cos(landL1Point[1] / 180 * Math.PI)) * 180 / Math.PI
      tempPoint[1] = landL1Point[1] + tempRadius * Math.cos(tempAngle) / 1000 / EARTH_RADIUS * 180 / Math.PI
      guidePointsArr.push(tempPoint)
    }

    // 添加引导线
    // L1点到L3，取L3点的id作为 airlineGuideLineMap 的sessionId
    let sessionId = this.getFeatureProperty(lastLandPoint, 'id')
    let currentGuideLine = this.airlineGuideLineMap.get(sessionId)
    if (!currentGuideLine) {
      let guideLine = new Line(this.ol2d, this.ol3d, this.lineSource)
      guideLine.addGuideLine(sessionId, guidePointsArr, style)
      // 3d
      let positions = []
      guidePointsArr.forEach((item, index) => {
        positions[index] = new Cesium.Cartesian3.fromDegrees(item[0], item[1], item[2] || 100)
      })
      let guideLine3d = new Line3d(this.entities, 'guideLine_' + sessionId, style, true, false)
      guideLine3d.positions = positions
      this.airlineGuideLineMap.set(sessionId, { 'item2d': guideLine, 'item3d': guideLine3d })
    } else {
      let coordinates = []
      let positions = []
      guidePointsArr.forEach(item => {
        coordinates.push(fromLonLat(item))
        positions.push(new Cesium.Cartesian3.fromDegrees(item[0], item[1], item[2] || 100))
      })
      currentGuideLine.item2d.guideLine.setCoordinates(coordinates)
      currentGuideLine.item3d.positions = positions
    }
  }

  /**
   * @desc: 获取两点之间的角度 ==> 最新
   * @param {*} b360
   * @param {Array} startPoint 起始坐标 [lng, lat, h]
   * @param {Array} endPoint 结束坐标 [lng, lat, h]
   * @return {*}
   * @use: 使用示例
   */
  getAngleOf2Point = function (b360, startPoint, endPoint) {
    var dis = getDistance(startPoint, endPoint) // 直线距离
    var deltY = (endPoint[1] - startPoint[1]) * Math.PI / 180 * EARTH_RADIUS * 1000
    var AC = deltY / dis
    var angle = 0
    if (Math.abs(AC) < 1) { angle = Math.acos(AC) } else if (endPoint[1] < startPoint[1]) { angle = Math.PI }
    if (endPoint[0] < startPoint[0]) {
      if (b360) { angle = Math.PI * 2 - angle } else { angle = -angle }
    }
    return angle
  }

  /**
   * @desc: java修改点属性(主要修改 latlng isHover radius)，地图对应发生变化
   * @param {Number} id 当前点id
   * @param {Array} latlng 当前点经纬度高度
   * @param {Number} type 当前点类型
   * @param {Number} index 当前点索引
   * @param {Number} isHover 当前点是否盘旋
   * @param {Number} radius 当前点半径
   * @param {Number} parentId 父级哈希 Map id（航线sessionId）
   * @param {Number} parentType 父级哈希 Map 类型（航线类型 1-单线飞行）
   * @return {*}
   * @use: 使用示例
   */
  updatePointProperties (id, latlng, type, index, isHover, radius, parentId, parentType) {
    let currentPointMap = null
    // 单线飞行
    if (parentType == 1 || parentType == 100) currentPointMap = this.airlinePointMap.get(parentId)
    let coordinate = fromLonLat(latlng)
    let currentPointFeature = null
    let style = null
    // 0-起始点 1-任务航点 2-降高 3-刹车 4-降落
    if (type < 2) {
      currentPointFeature = currentPointMap.taskPoint[index].feature
      let properties = currentPointFeature.getProperties()
      properties.isHover = isHover
      properties.radius = radius
      style = properties.style

      currentPointFeature.setProperties(properties)
      currentPointFeature.getGeometry().setCoordinates(coordinate)
    }
    if (type >= 2) {
      currentPointFeature = currentPointMap.landPoint[index].feature
      let properties = currentPointFeature.getProperties()
      properties.isHover = isHover
      properties.radius = radius
      style = properties.style

      currentPointFeature.setProperties(properties)
      currentPointFeature.getGeometry().setCoordinates(coordinate)
    }

    this.updateCircleProperties(id, latlng, isHover, radius, style)
    this.changePointPosition(id, latlng, type, index, isHover, radius, parentId, parentType)
  }

  /**
   * @desc: 修改Circle属性
   * @param {Number} id Circle id和点id相同
   * @param {Array} latlng
   * @param {Number} isHover
   * @param {Number} radius
   * @return {*}
   * @use: 使用示例
   */
  updateCircleProperties (id, latlng, isHover, radius, style) {
    let coordinate = fromLonLat(latlng)
    let arcRadius = radius / Math.cos(latlng[1] / 180 * Math.PI)
    let currentCircle = this.airlineCircleMap.get(id)

    let circleFillColor = color2Rgba(style.fillColor)
    let circleStyle = { ...style }
    circleStyle.fillColor = circleFillColor

    // 判断当前是否有circle，没有则添加
    if (!currentCircle) {
      // 1、（原来不盘旋 isHover=0 || radius=0 现在盘旋 isHover=1 && radius>0）
      if (isHover && radius > 0) {
        let circle = new Circle(this.ol2d, this.circleSource)
        circle.addCircle(id, 1, latlng, isHover, radius, circleStyle, false)

        // 3d
        let circle3d = null
        this.airlineCircleMap.set(id, { 'item2d': circle, 'item3d': circle3d })
      }
    } else {
      let feature = currentCircle.item2d.feature
      let properties = feature.getProperties()
      properties.isHover = isHover
      properties.radius = radius
      feature.setProperties(properties)

      if (isHover) feature.getGeometry().setCenterAndRadius(coordinate, arcRadius)
      else feature.getGeometry().setCenterAndRadius(coordinate, 0)
    }
  }

  /**
   * @desc: 删除单线飞行及机载航线所有的航线
   * @param {Number} sessionId 单线飞行和机载航线 sessionId 都来自于飞机，实则和飞机sessionId绑定
   * @return {*}
   * @use: 使用示例
   */
  delAllAirLineHashMap (sessionId) {
    this.delAirLineHashMap(sessionId, 1)
    // 其他
    this.idArr = []
  }

  /**
   * @desc: 清除整条航线（hash Map）
   * @param {Number} sessionId
   * @param {Number} lineType
   * @return {*}
   * @use: 使用示例
   */
  delAirLineHashMap (sessionId, lineType) {
    this.delAirLineTask(sessionId, lineType)
    this.delAirLineLand(sessionId, lineType)
    if (lineType == 1 || lineType == 100) {
      this.airlinePointMap.delete(sessionId)
      this.airlineTaskLineMap.delete(sessionId)
      this.airlineLandLineMap.delete(sessionId)
      this.airlineCircleMap.delete(sessionId)
      this.airlineGuideLineMap.delete(sessionId)
    }
    this.idArr = []
  }

  /**
   * @desc: 清除当前航线的任务相关点线
   * @param {Number} sessionId
   * @param {Number} lineType
   * @return {*}
   * @use: 使用示例
   */
  delAirLineTask (sessionId, lineType) {
    let currentAirLinePointMap = this.getAirLine(sessionId, lineType)
    if (!currentAirLinePointMap) return false

    // 清除任务点
    let taskPointList = currentAirLinePointMap.taskPoint // 任务点list
    taskPointList.forEach(item => {
      // 2d
      this.pointSource.removeFeature(item.feature)
      let id = item.feature.getProperties()['id']
      let currentCircleMap = this.airlineCircleMap.get(id)
      if (currentCircleMap) {
        this.circleSource.removeFeature(currentCircleMap.item2d.feature)
        this.airlineCircleMap.delete(id)
      }
      // 3d
      this.entities.removeById('airPoint3d_' + id)
      this.entities.removeById('airCircle3d_' + id)
    })
    currentAirLinePointMap.taskPoint = []

    // 清除任务线
    let taskLineMap = this.airlineTaskLineMap.get(sessionId)
    if (taskLineMap) {
      this.lineSource.removeFeature(taskLineMap.item2d.feature)
      this.airlineTaskLineMap.delete(sessionId)
      this.entities.removeById('airLine3dTask_' + sessionId)
    }
    if (this.hasLandLine) { // 非旋翼，有降落航线
      // 删除引导线
      let guideId = currentAirLinePointMap.landPoint[0].feature.getProperties()['id']
      let guideLineMap = this.airlineGuideLineMap.get(guideId).item2d
      this.lineSource.removeFeature(guideLineMap.feature)
      this.airlineGuideLineMap.delete(guideId)
      this.entities.removeById('guideLine_' + guideId)
      this.airlineGuideLineMap.delete(guideId)
    }

    this.idArr.splice(0, this.idArr.length - 3)
  }

  /**
   * @desc: 清除当前航线的降落相关点线
   * @param {Number} sessionId
   * @param {Number} lineType
   * @return {*}
   * @use: 使用示例
   */
  delAirLineLand (sessionId, lineType) {
    let currentAirLinePointMap = this.getAirLine(sessionId, lineType)
    if (!currentAirLinePointMap) return false

    // 清除降落点及circle
    let landPointList = currentAirLinePointMap.landPoint // 降落点list
    landPointList.forEach(item => {
      // 2d
      this.pointSource.removeFeature(item.feature)
      let id = item.feature.getProperties()['id']
      let currentCircleMap = this.airlineCircleMap.get(id)
      if (currentCircleMap) {
        this.circleSource.removeFeature(currentCircleMap.item2d.feature)
        this.airlineCircleMap.delete(id)
      }
      // 3d
      this.entities.removeById('airPoint3d_' + id)
      this.entities.removeById('airCircle3d_' + id)
    })
    currentAirLinePointMap.landPoint = []

    // 清除降落线
    let landLineMap = this.airlineLandLineMap.get(sessionId)
    if (landLineMap) {
      this.lineSource.removeFeature(landLineMap.item2d.feature)
      this.airlineLandLineMap.delete(sessionId)
      this.entities.removeById('airLine3dLand_' + sessionId)
    }
    // 删除引导线
    for (let value of this.airlineGuideLineMap.values()) {
      let feature = value.item2d.feature
      let id = feature.getProperties()['id']
      this.lineSource.removeFeature(feature)
      this.entities.removeById('guideLine_' + id)
    }
    this.airlineGuideLineMap.clear()

    this.idArr.splice(this.idArr.length - 3, this.idArr.length)
  }

  /**
   * @desc: 清除当前航线的引导线
   * @param {Number} sessionId
   * @param {Number} lineType
   * @return {*}
   * @use: 使用示例
   */
  delAirlineGuide (sessionId, lineType) {
    let currentAirLine = this.getAirLine(sessionId, lineType)
    let guideLineList = currentAirLine.guideLine // 引导线list
    if (guideLineList.length <= 0) return false

    guideLineList.forEach(item => {
      this.lineSource.removeFeature(item)
    })
    currentAirLine.guideLine = []
  }

  /**
   * @desc: 获取当前航线 hash Map
   * @param {Number} sessionId
   * @param {Number} lineType
   * @return {Object}
   * @use: 使用示例
   */
  getAirLine (sessionId, lineType) {
    let currentAirLinePointMap = null
    if (lineType == 1 || lineType == 100) {
      currentAirLinePointMap = this.airlinePointMap.get(sessionId)
    }
    return currentAirLinePointMap
  }

  // --------------------- 机载航线相关 ---------------------

  /**
   * @desc: 加载机载航线（最新）
   * @param {Number} sessionId
   * @param {Array} pointList [S, 2, 3, 4, ......, L1, L2, L3]
   * @return {*}
   * @use: 使用示例
   */
  loadAirLineFromUav (sessionId, lineType, pointList) {
    this.airlinePointMap.set(sessionId, { 'type': lineType, 'taskPoint': [], 'landPoint': [] })

    let currentairlinePointMap = this.airlinePointMap.get(sessionId)
    let currentTaskPointList = currentairlinePointMap.taskPoint
    let currentLandPointList = currentairlinePointMap.landPoint

    let len = pointList.length
    for (let i = len - 1; i >= 0; i--) {
      if (pointList[i].pointType >= 2) {
        this.hasLandLine = true
        break
      }
    }

    // 添加任务点/线（旋翼没有降落航线）
    let taskPoints = []
    if (this.hasLandLine) taskPoints = pointList.slice(0, pointList.length - 3)
    else taskPoints = pointList.slice(0, pointList.length)

    for (let i = 0; i < taskPoints.length; i++) {
      this.addAirinePoint(taskPoints[i].id, taskPoints[i].latlng, taskPoints[i].pointType, taskPoints[i].num, taskPoints[i].numPrefix || '', taskPoints[i].isHover, taskPoints[i].radius, taskPoints[i].isEdit || 0, taskPoints[i].pointStyle || null, sessionId, LINE_TYPE.FROM_UAV, currentTaskPointList)
    }

    // 添加降落点/线
    if (!this.hasLandLine) return false
    let landPoints = pointList.slice(pointList.length - 3, pointList.length)
    for (let j = 0; j < landPoints.length; j++) {
      this.addAirinePoint(landPoints[j].id, landPoints[j].latlng, landPoints[j].pointType, landPoints[j].num, landPoints[j].numPrefix || '', landPoints[j].isHover, landPoints[j].radius, taskPoints[j].isEdit || 0, landPoints[j].pointStyle || null, sessionId, LINE_TYPE.FROM_UAV, currentLandPointList)
    }
  }

  /**
   * @desc: 编辑机载航线(批量修改点是否可编辑属性)
   * @param {Number} sessionId
   * @return {*}
   * @use: 使用示例
   */
  editLoadAirLineFromUav (sessionId) {
    let currentAirLinePointMap = this.airlinePointMap.get(sessionId)
    if (!currentAirLinePointMap || currentAirLinePointMap.size <= 0) return false

    let taskPointList = currentAirLinePointMap.taskPoint
    let landPointList = currentAirLinePointMap.landPoint
    taskPointList.forEach(item => {
      let properties = item.feature.getProperties()
      properties.isEdit = 1
      properties.parentType = 1 // 机载航线类型转化为单线飞行（非可编辑 ==> 可编辑）
      item.feature.setProperties(properties)

      this.idArr.push(properties.id)
    })
    landPointList.forEach(item => {
      let properties = item.feature.getProperties()
      properties.isEdit = 1
      properties.parentType = 1 // 机载航线类型转化为单线飞行（非可编辑 ==> 可编辑）
      item.feature.setProperties(properties)

      this.idArr.push(properties.id)
    })
    // 更新线可编辑（支持多条线不同编辑状态）
    let taskLineMap = this.airlineTaskLineMap.get(sessionId)
    if (!taskLineMap || taskLineMap.size <= 0) return false
    let currentLineFeature = taskLineMap.item2d.feature
    let properties = currentLineFeature.getProperties()
    properties.isEdit = 1
    currentLineFeature.setProperties(properties)
  }

  addPlayBackTrackLine (sessionId, latlng) {
    this.trackLineInstance = new TrackLine(this.ol2d, this.ol3d, this.trackLineMap, this.lineSource)
    this.trackLineInstance.addPlayBackTrackLine(sessionId, latlng)
  }

  // --------------------- 电子围栏 ---------------------
  /**
   * @desc: 绘制圆形电子围栏
   * @param {Number} sessionId
   * @param {Array} latlng
   * @param {Number} radius
   * @param {Object} style
   * @return {*}
   * @use: 使用示例
   */
  drawCircleElectricFence (sessionId, latlng, radius, style) {
    if (!this.canSetCircleElectricFenceRadius) return false

    this.circleElectricFenceSessionId = sessionId
    this.electricFenceInstance.drawCircleElectricFence(sessionId, latlng, radius, style)
  }

  /**
   * @desc: 渲染圆形电子围栏
   * @param {Number} sessionId
   * @param {Array} latlng
   * @param {Number} radius
   * @param {Object} style
   */
  renderCircleElectricFence (sessionId, latlng, radius, style) {
    this.electricFenceInstance.drawCircleElectricFence(sessionId, latlng, radius, style)
  }

  /**
   * @desc: 是否显示圆形电子围栏
   * @param {Number} sessionId
   * @param {Number} isShow 1-显示 0-不显示
   * @return {*}
   * @use: 使用示例
   */
  switchShowCircleElectricFence (sessionId, isShow) {
    this.electricFenceInstance.switchShowCircleElectricFence(sessionId, isShow)
  }

  /**
   * @desc: 删除圆形电子围栏
   * @param {Number} sessionId
   * @return {*}
   * @use: 使用示例
   */
  delCircleElectricFence (sessionId) {
    this.electricFenceInstance.delCircleElectricFence(sessionId)
  }

  /**
   * @desc: 删除所有圆形电子围栏
   */
  delAllCircleElectricFence () {
    this.electricFenceInstance.delAllCircleElectricFence()
  }

  /**
   * @desc: 绘制多边形电子围栏
   * @param {*} sessionId
   * @param {*} latlng
   * @return {*}
   * @use: 使用示例
   */
  drawPolygonElectricFence (sessionId, latlng) {
    this.electricFenceInstance.drawPolygonElectricFence(sessionId, latlng)
    this.polygonElectricFenceSessionId = sessionId
  }

  /**
   * @desc: 渲染多边形电子围栏
   * @param {Number} sessionId
   * @param {Number} latlng [[lon, lat, h], [lon, lat, h], ......, [lon, lat, h]]
   */
  renderPolygonElectricFence (sessionId, latlng) {
    this.electricFenceInstance.renderPolygonElectricFence(sessionId, latlng)
    this.polygonElectricFenceSessionId = sessionId
  }

  /**
   * @desc: 更新多边形电子围栏
   * @param {Array} latlng
   * @param {Number} index
   * @param {Number} parentId
   */
  updatePolygonElectricFencePoint (latlng, index, parentId) {
    this.electricFenceInstance.updatePolygonElectricFencePoint(latlng, index, parentId)
  }

  delPolygonElectricFence (sessionId) {
    this.electricFenceInstance.delPolygonElectricFence(sessionId)
  }

  // --------------------- 其他自定义通用方法 ---------------------
  /**
   * @desc: 获取geom中feature指定属性
   * @param {*} geom
   * @param {*} key
   * @return {*}
   * @use: 使用示例
   */
  getFeatureProperty (geom, key) {
    if (!geom || !geom.feature) {
      console.error("geom or feature undefined")
      return false
    }
    return geom.feature.getProperties()[key]
  }

  // --------------------- Java ==> Js 交互模拟 ---------------------
  // 点击地图添加起飞点
  javaToJs_addTakeOffPoint () {
    this.setMapMode(500, 1646794856271, '单击地图打起飞点')
  }
  // 直接渲染起飞点
  javaToJs_renderTakeOffPoint () {
    this.addTakeOffPoint(1646794856271, [115.99193826981352, 39.999296027826034, 100])
  }
  // 删除起飞点
  javaToJs_delTakeOffPoint () {
    this.delTakeOffPoint(1646794856271)
  }
  // 指点飞行
  javaToJs_givePointToFly () {
    this.setMapMode(10, 1646191329544, '双击地图打点 双击地图打点 双击地图打点 双击地图打点 双击地图打点')
  }
  // 删除指点飞行
  javaToJs_delGivePointToFly () {
    this.delGivePointToFly(1646191329544)
  }
  // 自定义点
  javaToJs_addCustomPoint () {
    this.setMapMode(100, null, '单击地图打自定义点')
  }
  javaToJs_delCustomPoint () {
    let tempArr = []
    for (let key of this.customPointMap.keys()) {
      tempArr.push(key)
    }
    let random = Math.floor(Math.random() * tempArr.length)
    this.delCustomPoint(tempArr[random])
  }
  // 单线飞行 sessionId: 1646191322345 lineType: 1
  javaToJs_addSingleLineToFly () {
    this.setMapMode(1, 1646191322345, '单击地图打点连线')
  }
  // 更新点属性
  javaToJs_updateAirLineProperties (isHover) {
    // id, latlng, type, index, isHover, radius, parentId, parentType
    // // s1
    // setTimeout(() => {
    //   // id, latlng, type, index, isHover, radius, parentId, parentType
    //   this.updatePointProperties(this.idArr[0], [116.01676785061134, 39.99244825077764, 100], 0, 0, 1, 160, 1646191322345, 1)
    // }, 1000)

    // // 任务点 index=2 修改是否盘旋和半径属性
    // setTimeout(() => {
    //   this.updatePointProperties(this.idArr[2], [116.02303481101988, 40.012971457537844, 100], 1, 2, 1, 200, 1646191322345, 1)
    // }, 2000)
    // setTimeout(() => {
    //   this.updatePointProperties(this.idArr[2], [116.02303481101988, 40.012971457537844, 100], 1, 2, 0, 200, 1646191322345, 1)
    // }, 30000)
    // setTimeout(() => {
    //   this.updatePointProperties(this.idArr[2], [116.02303481101988, 40.012971457537844, 100], 1, 2, 1, 400, 1646191322345, 1)
    // }, 5000)

    // // 降落点 e1
    // setTimeout(() => {
    //   this.updatePointProperties(1646191726908, [115.97926296004152, 40.00997750404744, 100], 2, 0, 1, 200, 1646191322345, 1)
    // }, 4000)

    // 设置 任务点 index=2 修改是否盘旋和半径属性
    this.updatePointProperties(this.idArr[2], [116.02303481101988, 40.012971457537844, 100], 1, 2, isHover, 200, 1646191322345, 1)
  }
  // 删除航线中的点
  javaToJs_delAirLinePoint () {
    // sessionId, lineType, id, pointType, index
    this.delAirLinePoint(1646191322345, 1, this.idArr[3], 1, 3)
  }
  javaToJs_batchDelAirLinePoint () {
    // sessionId, lineType, pointType, str
    // this.batchDelAirLinePoint(1646191322345, 1, 1, [3, 8, 5, 6, 13, 8])
    this.batchDelAirLinePoint(1646191322345, 1, 1, [1, 2, 3, 5, 4, 5])
  }
  // 删除航线中的任务相关
  javaToJs_delAirLineTask () {
    this.delAirLineTask(1646191322345, 1)
  }
  // 删除航线中降落相关
  javaToJs_delAirLineLand () {
    this.delAirLineLand(1646191322345, 1)
  }
  // 删除引导线相关
  javaToJs_delAirLineGuideLine () {
    this.delAirLineGuide(1646191322345, 1)
  }
  // 删除整条航线
  javaToJs_delAirLineHashMap () {
    this.delAirLineHashMap(1646191322345, 1)
  }
  // 删除所有航线
  javaToJs_delAllAirLineHashMap () {
    this.delAllAirLineHashMap(1646191322345)
  }
  // load机载航线
  javaToJs_loadAirLineFromUav () {
    let list = []
    let step = 0.00135;
    let latlng = [116.00286086666163, 40.002130757891564, 100]
    let { ...s1 } = { id: new Date().getTime() - Math.floor(10000000000 * Math.random(0, 1)), pointType: 0, num: 1, numPrefix: 'S', latlng: latlng, isHover: 0, radius: 160, isEdit: 1, speed: 10, pointStyle: { 'fillColor': 'rgba(255, 153, 255, 1)', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } };
    let { ...e1 } = { id: new Date().getTime() - Math.floor(10000000000 * Math.random(0, 1)), pointType: 2, num: 1, numPrefix: 'L', latlng: [latlng[0] - step * 10, latlng[1]], isHover: 1, radius: 160, isEdit: 1, speed: 10, pointStyle: { 'fillColor': 'rgba(255, 153, 255, 1)', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } };
    let { ...e2 } = { id: new Date().getTime() - Math.floor(10000000000 * Math.random(0, 1)), pointType: 3, num: 2, numPrefix: 'L', latlng: [latlng[0] - step * 10, latlng[1] + step * 3.8], isHover: 0, radius: 0, isEdit: 1, speed: 10, pointStyle: { 'fillColor': 'rgba(255, 153, 255, 1)', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } };
    let { ...e3 } = { id: new Date().getTime() - Math.floor(10000000000 * Math.random(0, 1)), pointType: 4, num: 3, numPrefix: 'L', latlng: [latlng[0] - step * 10, latlng[1] + step * 5], isHover: 0, radius: 0, isEdit: 1, speed: 10, pointStyle: { 'fillColor': 'rgba(255, 153, 255, 1)', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } };
    let startPointList = [s1, e1, e2, e3]
    // let startPointList = [s1]
    list = startPointList

    for (let i = 0; i < 1000; i++) {
      let latlng = [116.00286086666163 - Math.random(0, 1), 40.002130757891564 - Math.random(0, 1), 100 - 10 * Math.random(0, 1)]
      // let isHover = 10 * Math.random(0, 1) >= 5 ? 1 : 0
      let isHover = 0

      let pointItem = { id: new Date().getTime() - Math.floor(10000000000 * Math.random(0, 1)), pointType: 1, num: i + 2, numPrefix: '', latlng: latlng, isHover: isHover, radius: 160, isEdit: 1, speed: 10, pointStyle: { 'fillColor': 'rgba(255, 153, 255, 1)', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } }
      list.splice(i + 1, 0, pointItem)
    }

    this.setMapMode(2, 1646191322345) // 不可编辑模式
    this.delAirLineHashMap(1646191322345, 100)
    this.loadAirLineFromUav(1646191322345, 100, list)
  }

  // 编辑机载航线
  javaToJs_editLoadAirLineFromUav () {
    this.setMapMode(1, 1646191322345, '单击地图打点连线') // 不可编辑模式 ==> 可编辑模式
    this.editLoadAirLineFromUav(1646191322345)
  }

  // 圆形电子围栏
  javaToJs_drawCircleElectricFence () {
    this.setMapMode(600, 1646244851217, '鼠标左键点击地图绘制圆形电子围栏')
    this.circleElectricFenceSessionId = 1646244851217
    this.renderCircleElectricFence(1646244851217, [115.99810678466692, 39.99564911373906, 80], 500, { 'fillColor': 'rgba(251, 172, 29, 1)', 'strokeColor': 'rgba(251, 172, 29, 1)', 'strokeWidth': 2 })
  }

  javaToJs_drawCircleElectricFence2 () {
    this.setMapMode(600, 1646244851218, '鼠标左键点击地图绘制圆形电子围栏')
    this.circleElectricFenceSessionId = 1646244851218
  }

  javaToJs_switchShowCircleElectricFence (isShow) {
    this.switchShowCircleElectricFence(1646244851217, isShow)
  }

  javaToJs_switchShowCircleElectricFence2 (isShow) {
    this.switchShowCircleElectricFence(1646244851218, isShow)
  }

  javaToJs_delCircleElectricFence1 () {
    this.delCircleElectricFence(1646244851217)
  }

  javaToJs_delCircleElectricFence2 () {
    this.delCircleElectricFence(1646244851218)
  }

  javaToJs_drawPolygonElectricFence1 () {
    this.setMapMode(601, 1646244851219, '鼠标左键点击地图并移动开始绘制，再次点击结束绘制')
    this.polygonElectricFenceSessionId = 1646244851219
    // this.renderPolygonElectricFence(1646244851219, [[115.99656113039237, 39.99721811302112, 80], [116.00102860096376, 39.99259258256427, 80], [115.99382445472293, 39.98819007801126, 80], [115.98384750288095, 39.99005459196684, 80], [115.98335524025606, 39.99806104816747, 80]])
  }

  javaToJs_drawPolygonElectricFence2 () {
    this.setMapMode(601, 1646244851220, '鼠标左键点击地图并移动开始绘制，再次点击结束绘制')
    this.polygonElectricFenceSessionId = 1646244851220
  }

  javaToJs_delPolygonElectricFence1 () {
    this.delPolygonElectricFence(1646244851219)
  }

  javaToJs_delPolygonElectricFence2 () {
    this.delPolygonElectricFence(1646244851220)
  }

  // 点击地图添加仿真起飞点
  javaToJs_addSimulationTakeOffPoint () {
    this.setMapMode(551, 1648868306127, '单击地图打仿真起飞点')
  }
  // 直接渲染仿真起飞点
  javaToJs_renderSimulationTakeOffPoint () {
    this.addSimulationTakeOffPoint(1648868306127, [115.98357240619953, 40.003436651598776, 80])
  }
  // 删除仿真起飞点
  javaToJs_delSimulationTakeOffPoint () {
    this.delSimulationTakeOffPoint(1648868306127)
  }

  // 渲染消防员
  javaToJs_renderFiremen () {
    let list = [{ sessionId: 1649747792730, latlng: [115.998315, 39.998828, 335.41], text: '消防员-叶明', showOverlay: true, style: { 'fillColor': 'red', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } }, { sessionId: 1649747792731, latlng: [116.002633, 39.996777, 332.04], text: '消防员-张钊', showOverlay: true, style: { 'fillColor': 'red', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } }, { sessionId: 1649747792732, latlng: [116.000966, 39.993696, 521.91], text: '消防员-魏佳航', showOverlay: true, style: { 'fillColor': 'red', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } }, { sessionId: 1649833565656, latlng: [116.001977, 39.991114, 468.88], text: '消防员-孙天伟', showOverlay: true, style: { 'fillColor': 'green', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } }, { sessionId: 1649833565657, latlng: [115.998008, 39.988647, 454.44], text: '消防员-郭志邦', showOverlay: true, style: { 'fillColor': 'green', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } }, { sessionId: 1649833565658, latlng: [115.995084, 39.991676, 434.4], text: '消防员-鞠国栋', showOverlay: true, style: { 'fillColor': 'green', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } }]

    list.forEach(item => {
      let _latlng = [...item.latlng]
      _latlng[2] = item.latlng[2] + 100
      this.addCustomPoint(item.sessionId, item.latlng, 4, FIREMAN_POINT, 0, false, item.style, item.showOverlay, item.text, [0, -50])
    })

    // setTimeout(() => {
    //   this.updateCustomPoint(1649747792730, [115.994943, 39.998415, 372.02], 0, { 'fillColor': 'green', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 }, true, '警察-叶明')
    // }, 8000)

    // setTimeout(() => {
    //   this.delCustomPoint(1649747792730)
    // }, 12000)
  }

  javaToJs_updateFiremen (sessionId, latlng) {
    this.updateCustomPoint(sessionId, latlng, 0, null, true, '', true)
  }

  javaToJs_delAllFiremen () {
    let list = [{ sessionId: 1649747792730, latlng: [115.998315, 39.998828, 335.41], text: '消防员-叶明', showOverlay: true, style: { 'fillColor': 'red', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } }, { sessionId: 1649747792731, latlng: [116.002633, 39.996777, 332.04], text: '消防员-张钊', showOverlay: true, style: { 'fillColor': 'red', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } }, { sessionId: 1649747792732, latlng: [116.000966, 39.993696, 521.91], text: '消防员-魏佳航', showOverlay: true, style: { 'fillColor': 'red', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } }, { sessionId: 1649833565656, latlng: [116.001977, 39.991114, 468.88], text: '消防员-孙天伟', showOverlay: true, style: { 'fillColor': 'green', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } }, { sessionId: 1649833565657, latlng: [115.998008, 39.988647, 454.44], text: '消防员-郭志邦', showOverlay: true, style: { 'fillColor': 'green', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } }, { sessionId: 1649833565658, latlng: [115.995084, 39.991676, 434.4], text: '消防员-鞠国栋', showOverlay: true, style: { 'fillColor': 'green', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 } }]

    list.forEach(item => {
      this.delCustomPoint(item.sessionId)
    })
  }

  // 删除指定消防员，id字符串 "1649747792730,1649747792732,1649833565658"
  javaToJs_delFiremen () {
    let ids = "1649747792730,1649747792732,1649833565658"
    let idArr = ids.split(',')
    idArr.forEach(item => {
      this.delCustomPoint(Number(item))
    })
  }

  javaToJs_renderAircrew () {
    let list3 = [{ sessionId: 1649903662931, latlng: [115.989589, 39.999484, 389.51], text: '机组1-1号' }, { sessionId: 1649903662932, latlng: [115.986668, 39.996652, 466.38], text: '机组1-2号' }, { sessionId: 1649903662933, latlng: [115.986775, 39.989484, 306.86], text: '机组1-3号' }]

    list3.forEach(item => {
      this.addCustomPoint(item.sessionId, item.latlng, 300, FIREMAN_POINT, 0, false, { 'fillColor': '#D39000', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 }, true, item.text, [0, -50])
    })

    let list4 = [{ sessionId: 1649908080422, latlng: [116.007127, 39.989096, 307.73], text: '机组2-1号' }, { sessionId: 1649908080423, latlng: [116.009415, 39.995962, 434.56], text: '机组2-2号' }, { sessionId: 1649908080424, latlng: [116.009998, 40.00013, 261.1], text: '机组2-3号' }]

    list4.forEach(item => {
      this.addCustomPoint(item.sessionId, item.latlng, 301, FIREMAN_POINT, 0, false, { 'fillColor': '#839700', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 }, true, item.text, [0, -50])
    })

    let list5 = [{ sessionId: 1649908081477, latlng: [115.992096, 39.994301, 513.55], text: '机组3-1号' }, { sessionId: 1649908081478, latlng: [115.996755, 39.98674, 248.37], text: '机组3-2号' }, { sessionId: 1649908081479, latlng: [116.010992, 39.9926, 437.93], text: '机组3-3号' }]

    list5.forEach(item => {
      this.addCustomPoint(item.sessionId, item.latlng, 302, FIREMAN_POINT, 0, false, { 'fillColor': '#008269', 'strokeColor': 'rgba(255, 255, 255, 1)', 'strokeWidth': 2 }, true, item.text, [0, -50])
    })
  }

  javaToJs_updateAircrew1 (sessionId, latlng, yaw, hpr, showTrack, type) {
    this.updateAirGroundCooAircrew(sessionId, latlng, yaw, hpr, showTrack, type)
  }

  javaToJs_delAircrew () {
    let ids = "1649903662931,1649908080422,1649908081477"
    let idArr = ids.split(',')
    idArr.forEach(item => {
      this.delAirGroundCooAircrew(Number(item))
    })
  }
}

export default OlcsMap
